All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Search JAR files by class name

Source code of the class FileUtil.java part of aspectjweaver version 1.8.9

Go to download Show more of this group Show artifacts with the name aspectjweaver
        /* *******************************************************************
 * Copyright (c) 1999-2001 Xerox Corporation, 
 *               2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors: 
 *     Xerox/PARC     initial implementation 
 * ******************************************************************/

package org.aspectj.util;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author Andy Clement
 * @author Kris De Volder
 */
public class FileUtil {
	/** default parent directory File when a file has a null parent */
	public static final File DEFAULT_PARENT = new File("."); // XXX user.dir?

	/** unmodifiable List of String source file suffixes (including leading ".") */
	public static final List SOURCE_SUFFIXES = Collections.unmodifiableList(Arrays.asList(new String[] { ".java", ".aj" }));

	public static final FileFilter ZIP_FILTER = new FileFilter() {
		public boolean accept(File file) {
			return isZipFile(file);
		}

		public String toString() {
			return "ZIP_FILTER";
		}
	};

	// public static final FileFilter SOURCE_FILTER = new FileFilter() {
	// public boolean accept(File file) {
	// return hasSourceSuffix(file);
	// }
	//
	// public String toString() {
	// return "SOURCE_FILTER";
	// }
	// };

	final static int[] INT_RA = new int[0];

	/** accept all files */
	public static final FileFilter ALL = new FileFilter() {
		public boolean accept(File f) {
			return true;
		}
	};
	public static final FileFilter DIRS_AND_WRITABLE_CLASSES = new FileFilter() {
		public boolean accept(File file) {
			return ((null != file) && (file.isDirectory() || (file.canWrite() && file.getName().toLowerCase().endsWith(".class"))));
		}
	};
	private static final boolean PERMIT_CVS;
	static {
		String name = FileUtil.class.getName() + ".PERMIT_CVS";
		PERMIT_CVS = LangUtil.getBoolean(name, false);
	}

	/** @return true if file exists and is a zip file */
	public static boolean isZipFile(File file) {
		try {
			return (null != file) && new ZipFile(file) != null;
		} catch (IOException e) {
			return false;
		}
	}

	/** @return true if path ends with .zip or .jar */
	// public static boolean hasZipSuffix(String path) {
	// return ((null != path) && (0 != zipSuffixLength(path)));
	// }
	/** @return 0 if file has no zip/jar suffix or 4 otherwise */
	public static int zipSuffixLength(File file) {
		return (null == file ? 0 : zipSuffixLength(file.getPath()));
	}

	/** @return 0 if no zip/jar suffix or 4 otherwise */
	public static int zipSuffixLength(String path) {
		if ((null != path) && (4 < path.length())) {
			String test = path.substring(path.length() - 4).toLowerCase();
			if (".zip".equals(test) || ".jar".equals(test)) {
				return 4;
			}
		}
		return 0;
	}

	/** @return true if file path has a source suffix */
	public static boolean hasSourceSuffix(File file) {
		return ((null != file) && hasSourceSuffix(file.getPath()));
	}

	/** @return true if path ends with .java or .aj */
	public static boolean hasSourceSuffix(String path) {
		return ((null != path) && (0 != sourceSuffixLength(path)));
	}

	/**
	 * @return 0 if file has no source suffix or the length of the suffix otherwise
	 */
	public static int sourceSuffixLength(File file) {
		return (null == file ? 0 : sourceSuffixLength(file.getPath()));
	}

	/** @return 0 if no source suffix or the length of the suffix otherwise */
	public static int sourceSuffixLength(String path) {
		if (LangUtil.isEmpty(path)) {
			return 0;
		}

		for (Iterator iter = SOURCE_SUFFIXES.iterator(); iter.hasNext();) {
			String suffix = iter.next();
			if (path.endsWith(suffix) || path.toLowerCase().endsWith(suffix)) {
				return suffix.length();
			}
		}
		return 0;
	}

	/** @return true if this is a readable directory */
	public static boolean canReadDir(File dir) {
		return ((null != dir) && dir.canRead() && dir.isDirectory());
	}

	/** @return true if this is a readable file */
	public static boolean canReadFile(File file) {
		return ((null != file) && file.canRead() && file.isFile());
	}

	/** @return true if dir is a writable directory */
	public static boolean canWriteDir(File dir) {
		return ((null != dir) && dir.canWrite() && dir.isDirectory());
	}

	/** @return true if this is a writable file */
	public static boolean canWriteFile(File file) {
		return ((null != file) && file.canWrite() && file.isFile());
	}

	// /**
	// * @throws IllegalArgumentException unless file is readable and not a
	// * directory
	// */
	// public static void throwIaxUnlessCanReadFile(File file, String label) {
	// if (!canReadFile(file)) {
	// throw new IllegalArgumentException(label + " not readable file: " +
	// file);
	// }
	// }

	/**
	 * @throws IllegalArgumentException unless dir is a readable directory
	 */
	public static void throwIaxUnlessCanReadDir(File dir, String label) {
		if (!canReadDir(dir)) {
			throw new IllegalArgumentException(label + " not readable dir: " + dir);
		}
	}

	/**
	 * @throws IllegalArgumentException unless file is readable and not a directory
	 */
	public static void throwIaxUnlessCanWriteFile(File file, String label) {
		if (!canWriteFile(file)) {
			throw new IllegalArgumentException(label + " not writable file: " + file);
		}
	}

	/** @throws IllegalArgumentException unless dir is a readable directory */
	public static void throwIaxUnlessCanWriteDir(File dir, String label) {
		if (!canWriteDir(dir)) {
			throw new IllegalArgumentException(label + " not writable dir: " + dir);
		}
	}

	/** @return array same length as input, with String paths */
	public static String[] getPaths(File[] files) {
		if ((null == files) || (0 == files.length)) {
			return new String[0];
		}
		String[] result = new String[files.length];
		for (int i = 0; i < result.length; i++) {
			if (null != files[i]) {
				result[i] = files[i].getPath();
			}
		}
		return result;
	}

	/** @return array same length as input, with String paths */
	public static String[] getPaths(List files) {
		final int size = (null == files ? 0 : files.size());
		if (0 == size) {
			return new String[0];
		}
		String[] result = new String[size];
		for (int i = 0; i < size; i++) {
			File file = files.get(i);
			if (null != file) {
				result[i] = file.getPath();
			}
		}
		return result;
	}

	/**
	 * Extract the name of a class from the path to its file. If the basedir is null, then the class is assumed to be in the default
	 * package unless the classFile has one of the top-level suffixes { com, org, java, javax } as a parent directory.
	 *
	 * @param basedir the File of the base directory (prefix of classFile)
	 * @param classFile the File of the class to extract the name for
	 * @throws IllegalArgumentException if classFile is null or does not end with ".class" or a non-null basedir is not a prefix of
	 *         classFile
	 */
	public static String fileToClassName(File basedir, File classFile) {
		LangUtil.throwIaxIfNull(classFile, "classFile");
		String classFilePath = normalizedPath(classFile);
		if (!classFilePath.endsWith(".class")) {
			String m = classFile + " does not end with .class";
			throw new IllegalArgumentException(m);
		}
		classFilePath = classFilePath.substring(0, classFilePath.length() - 6);
		if (null != basedir) {
			String basePath = normalizedPath(basedir);
			if (!classFilePath.startsWith(basePath)) {
				String m = classFile + " does not start with " + basedir;
				throw new IllegalArgumentException(m);
			}
			classFilePath = classFilePath.substring(basePath.length() + 1);
		} else {
			final String[] suffixes = new String[] { "com", "org", "java", "javax" };
			boolean found = false;
			for (int i = 0; !found && (i < suffixes.length); i++) {
				int loc = classFilePath.indexOf(suffixes[i] + "/");
				if ((0 == loc) || ((-1 != loc) && ('/' == classFilePath.charAt(loc - 1)))) {
					classFilePath = classFilePath.substring(loc);
					found = true;
				}
			}
			if (!found) {
				int loc = classFilePath.lastIndexOf("/");
				if (-1 != loc) { // treat as default package
					classFilePath = classFilePath.substring(loc + 1);
				}
			}
		}
		return classFilePath.replace('/', '.');
	}

	/**
	 * Normalize path for comparisons by rendering absolute, clipping basedir prefix, trimming and changing '\\' to '/'
	 *
	 * @param file the File with the path to normalize
	 * @param basedir the File for the prefix of the file to normalize - ignored if null
	 * @return "" if null or normalized path otherwise
	 * @throws IllegalArgumentException if basedir is not a prefix of file
	 */
	public static String normalizedPath(File file, File basedir) {
		String filePath = normalizedPath(file);
		if (null != basedir) {
			String basePath = normalizedPath(basedir);
			if (filePath.startsWith(basePath)) {
				filePath = filePath.substring(basePath.length());
				if (filePath.startsWith("/")) {
					filePath = filePath.substring(1);
				}
			}
		}
		return filePath;
	}

	/**
	 * Render a set of files to String as a path by getting absolute paths of each and delimiting with infix.
	 *
	 * @param files the File[] to flatten - may be null or empty
	 * @param infix the String delimiter internally between entries (if null, then use File.pathSeparator). (alias to
	 *        flatten(getAbsolutePaths(files), infix)
	 * @return String with absolute paths to entries in order, delimited with infix
	 */
	public static String flatten(File[] files, String infix) {
		if (LangUtil.isEmpty(files)) {
			return "";
		}
		return flatten(getPaths(files), infix);
	}

	/**
	 * Flatten File[] to String.
	 *
	 * @param files the File[] of paths to flatten - null ignored
	 * @param infix the String infix to use - null treated as File.pathSeparator
	 */
	public static String flatten(String[] paths, String infix) {
		if (null == infix) {
			infix = File.pathSeparator;
		}
		StringBuffer result = new StringBuffer();
		boolean first = true;
		for (int i = 0; i < paths.length; i++) {
			String path = paths[i];
			if (null == path) {
				continue;
			}
			if (first) {
				first = false;
			} else {
				result.append(infix);
			}
			result.append(path);
		}
		return result.toString();
	}

	/**
	 * Normalize path for comparisons by rendering absolute trimming and changing '\\' to '/'
	 *
	 * @return "" if null or normalized path otherwise
	 */
	public static String normalizedPath(File file) {
		return (null == file ? "" : weakNormalize(file.getAbsolutePath()));
	}

	/**
	 * Weakly normalize path for comparisons by trimming and changing '\\' to '/'
	 */
	public static String weakNormalize(String path) {
		if (null != path) {
			path = path.replace('\\', '/').trim();
		}
		return path;
	}

	/**
	 * Get best File for the first-readable path in input paths, treating entries prefixed "sp:" as system property keys. Safe to
	 * call in static initializers.
	 *
	 * @param paths the String[] of paths to check.
	 * @return null if not found, or valid File otherwise
	 */
	public static File getBestFile(String[] paths) {
		if (null == paths) {
			return null;
		}
		File result = null;
		for (int i = 0; (null == result) && (i < paths.length); i++) {
			String path = paths[i];
			if (null == path) {
				continue;
			}
			if (path.startsWith("sp:")) {
				try {
					path = System.getProperty(path.substring(3));
				} catch (Throwable t) {
					path = null;
				}
				if (null == path) {
					continue;
				}
			}
			try {
				File f = new File(path);
				if (f.exists() && f.canRead()) {
					result = FileUtil.getBestFile(f);
				}
			} catch (Throwable t) {
				// swallow
			}
		}
		return result;
	}

	/**
	 * Render as best file, canonical or absolute.
	 *
	 * @param file the File to get the best File for (not null)
	 * @return File of the best-available path
	 * @throws IllegalArgumentException if file is null
	 */
	public static File getBestFile(File file) {
		LangUtil.throwIaxIfNull(file, "file");
		if (file.exists()) {
			try {
				return file.getCanonicalFile();
			} catch (IOException e) {
				return file.getAbsoluteFile();
			}
		} else {
			return file;
		}
	}

	/**
	 * Render as best path, canonical or absolute.
	 *
	 * @param file the File to get the path for (not null)
	 * @return String of the best-available path
	 * @throws IllegalArgumentException if file is null
	 */
	public static String getBestPath(File file) {
		LangUtil.throwIaxIfNull(file, "file");
		if (file.exists()) {
			try {
				return file.getCanonicalPath();
			} catch (IOException e) {
				return file.getAbsolutePath();
			}
		} else {
			return file.getPath();
		}
	}

	/** @return array same length as input, with String absolute paths */
	public static String[] getAbsolutePaths(File[] files) {
		if ((null == files) || (0 == files.length)) {
			return new String[0];
		}
		String[] result = new String[files.length];
		for (int i = 0; i < result.length; i++) {
			if (null != files[i]) {
				result[i] = files[i].getAbsolutePath();
			}
		}
		return result;
	}

	/**
	 * Recursively delete the contents of dir, but not the dir itself
	 *
	 * @return the total number of files deleted
	 */
	public static int deleteContents(File dir) {
		return deleteContents(dir, ALL);
	}

	/**
	 * Recursively delete some contents of dir, but not the dir itself. This deletes any subdirectory which is empty after its files
	 * are deleted.
	 *
	 * @return the total number of files deleted
	 */
	public static int deleteContents(File dir, FileFilter filter) {
		return deleteContents(dir, filter, true);
	}

	/**
	 * Recursively delete some contents of dir, but not the dir itself. If deleteEmptyDirs is true, this deletes any subdirectory
	 * which is empty after its files are deleted.
	 *
	 * @param dir the File directory (if a file, the the file is deleted)
	 * @return the total number of files deleted
	 */
	public static int deleteContents(File dir, FileFilter filter,
			boolean deleteEmptyDirs) {
		if (null == dir) {
			throw new IllegalArgumentException("null dir");
		}
		if ((!dir.exists()) || (!dir.canWrite())) {
			return 0;
		}
		if (!dir.isDirectory()) {
			dir.delete();
			return 1;
		}
		String[] fromFiles = dir.list();
		if (fromFiles == null) {
			return 0;
		}
		int result = 0;
		for (int i = 0; i < fromFiles.length; i++) {
			String string = fromFiles[i];
			File file = new File(dir, string);
			if ((null == filter) || filter.accept(file)) {
				if (file.isDirectory()) {
					result += deleteContents(file, filter, deleteEmptyDirs);
					String[] fileContent = file.list();
					if (deleteEmptyDirs && fileContent != null
							&& 0 == fileContent.length) {
						file.delete();
					}
				} else {
					/* boolean ret = */
					file.delete();
					result++;
				}
			}
		}
		return result;
	}

	/**
	 * Copy contents of fromDir into toDir
	 *
	 * @param fromDir must exist and be readable
	 * @param toDir must exist or be creatable and be writable
	 * @return the total number of files copied
	 */
	public static int copyDir(File fromDir, File toDir) throws IOException {
		return copyDir(fromDir, toDir, null, null);
	}

	/**
	 * Recursively copy files in fromDir (with any fromSuffix) to toDir, replacing fromSuffix with toSuffix if any. This silently
	 * ignores dirs and files that are not readable but throw IOException for directories that are not writable. This does not clean
	 * out the original contents of toDir. (subdirectories are not renamed per directory rules)
	 *
	 * @param fromSuffix select files with this suffix - select all if null or empty
	 * @param toSuffix replace fromSuffix with toSuffix in the destination file name - ignored if null or empty, appended to name if
	 *        fromSuffix is null or empty
	 * @return the total number of files copied
	 */
	public static int copyDir(File fromDir, File toDir, final String fromSuffix, String toSuffix) throws IOException {
		return copyDir(fromDir, toDir, fromSuffix, toSuffix, (FileFilter) null);
	}

	// /**
	// * Recursively copy files in fromDir (with any fromSuffix) to toDir,
	// * replacing fromSuffix with toSuffix if any, and adding the destination
	// * file to any collector. This silently ignores dirs and files that are
	// not
	// * readable but throw IOException for directories that are not writable.
	// * This does not clean out the original contents of toDir. (subdirectories
	// * are not renamed per directory rules) This calls any delegate
	// * FilenameFilter to collect any selected file.
	// *
	// * @param fromSuffix select files with this suffix - select all if null or
	// * empty
	// * @param toSuffix replace fromSuffix with toSuffix in the destination
	// file
	// * name - ignored if null or empty, appended to name if
	// * fromSuffix is null or empty
	// * @param collector the List sink for destination files - ignored if null
	// * @return the total number of files copied
	// */
	// public static int copyDir(File fromDir, File toDir, final String
	// fromSuffix, final String toSuffix, final List collector)
	// throws IOException {
	// // int before = collector.size();
	// if (null == collector) {
	// return copyDir(fromDir, toDir, fromSuffix, toSuffix);
	// } else {
	// FileFilter collect = new FileFilter() {
	// public boolean accept(File pathname) {
	// return collector.add(pathname);
	// }
	// };
	// return copyDir(fromDir, toDir, fromSuffix, toSuffix, collect);
	// }
	// }

	/**
	 * Recursively copy files in fromDir (with any fromSuffix) to toDir, replacing fromSuffix with toSuffix if any. This silently
	 * ignores dirs and files that are not readable but throw IOException for directories that are not writable. This does not clean
	 * out the original contents of toDir. (subdirectories are not renamed per directory rules) This calls any delegate
	 * FilenameFilter to collect any selected file.
	 *
	 * @param fromSuffix select files with this suffix - select all if null or empty
	 * @param toSuffix replace fromSuffix with toSuffix in the destination file name - ignored if null or empty, appended to name if
	 *        fromSuffix is null or empty
	 * @return the total number of files copied
	 */
	public static int copyDir(File fromDir, File toDir, final String fromSuffix, final String toSuffix, final FileFilter delegate)
			throws IOException {

		if ((null == fromDir) || (!fromDir.canRead())) {
			return 0;
		}
		final boolean haveSuffix = ((null != fromSuffix) && (0 < fromSuffix.length()));
		final int slen = (!haveSuffix ? 0 : fromSuffix.length());

		if (!toDir.exists()) {
			toDir.mkdirs();
		}
		final String[] fromFiles;
		if (!haveSuffix) {
			fromFiles = fromDir.list();
		} else {
			FilenameFilter filter = new FilenameFilter() {
				public boolean accept(File dir, String name) {
					return (new File(dir, name).isDirectory() || (name.endsWith(fromSuffix)));
				}
			};
			fromFiles = fromDir.list(filter);
		}
		int result = 0;
		final int MAX = (null == fromFiles ? 0 : fromFiles.length);
		for (int i = 0; i < MAX; i++) {
			String filename = fromFiles[i];
			File fromFile = new File(fromDir, filename);
			if (fromFile.canRead()) {
				if (fromFile.isDirectory()) {
					result += copyDir(fromFile, new File(toDir, filename), fromSuffix, toSuffix, delegate);
				} else if (fromFile.isFile()) {
					if (haveSuffix) {
						filename = filename.substring(0, filename.length() - slen);
					}
					if (null != toSuffix) {
						filename = filename + toSuffix;
					}
					File targetFile = new File(toDir, filename);
					if ((null == delegate) || delegate.accept(targetFile)) {
						copyFile(fromFile, targetFile);
					}
					result++;
				}
			}
		}
		return result;
	}

	/**
	 * Recursively list files in srcDir.
	 *
	 * @return ArrayList with String paths of File under srcDir (relative to srcDir)
	 */
	public static String[] listFiles(File srcDir) {
		ArrayList result = new ArrayList();
		if ((null != srcDir) && srcDir.canRead()) {
			listFiles(srcDir, null, result);
		}
		return result.toArray(new String[0]);
	}

	public static final FileFilter aspectjSourceFileFilter = new FileFilter() {
		public boolean accept(File pathname) {
			String name = pathname.getName().toLowerCase();
			return name.endsWith(".java") || name.endsWith(".aj");
		}
	};

	/**
	 * Recursively list files in srcDir.
	 *
	 * @return ArrayList with String paths of File under srcDir (relative to srcDir)
	 */
	public static File[] listFiles(File srcDir, FileFilter fileFilter) {
		ArrayList result = new ArrayList();
		if ((null != srcDir) && srcDir.canRead()) {
			listFiles(srcDir, result, fileFilter);
		}
		return result.toArray(new File[result.size()]);
	}

	/**
	 * Recursively list .class files in specified directory
	 *
	 * @return List of File objects
	 */
	public static List listClassFiles(File dir) {
		ArrayList result = new ArrayList();
		if ((null != dir) && dir.canRead()) {
			listClassFiles(dir, result);
		}
		return result;
	}

	/**
	 * Convert String[] paths to File[] as offset of base directory
	 *
	 * @param basedir the non-null File base directory for File to create with paths
	 * @param paths the String[] of paths to create
	 * @return File[] with same length as paths
	 */
	public static File[] getBaseDirFiles(File basedir, String[] paths) {
		return getBaseDirFiles(basedir, paths, (String[]) null);
	}

	/**
	 * Convert String[] paths to File[] as offset of base directory
	 *
	 * @param basedir the non-null File base directory for File to create with paths
	 * @param paths the String[] of paths to create
	 * @param suffixes the String[] of suffixes to limit sources to - ignored if null
	 * @return File[] with same length as paths
	 */
	public static File[] getBaseDirFiles(File basedir, String[] paths, String[] suffixes) {
		LangUtil.throwIaxIfNull(basedir, "basedir");
		LangUtil.throwIaxIfNull(paths, "paths");
		File[] result = null;
		if (!LangUtil.isEmpty(suffixes)) {
			ArrayList list = new ArrayList();
			for (int i = 0; i < paths.length; i++) {
				String path = paths[i];
				for (int j = 0; j < suffixes.length; j++) {
					if (path.endsWith(suffixes[j])) {
						list.add(new File(basedir, paths[i]));
						break;
					}
				}
			}
			result = list.toArray(new File[0]);
		} else {
			result = new File[paths.length];
			for (int i = 0; i < result.length; i++) {
				result[i] = newFile(basedir, paths[i]);
			}
		}
		return result;
	}

	/**
	 * Create a new File, resolving paths ".." and "." specially.
	 *
	 * @param dir the File for the parent directory of the file
	 * @param path the path in the parent directory (filename only?)
	 * @return File for the new file.
	 */
	private static File newFile(File dir, String path) {
		if (".".equals(path)) {
			return dir;
		} else if ("..".equals(path)) {
			File parentDir = dir.getParentFile();
			if (null != parentDir) {
				return parentDir;
			} else {
				return new File(dir, "..");
			}
		} else {
			return new File(dir, path);
		}
	}

	/**
	 * Copy files from source dir into destination directory, creating any needed directories. This differs from copyDir in not
	 * being recursive; each input with the source dir creates a full path. However, if the source is a directory, it is copied as
	 * such.
	 *
	 * @param srcDir an existing, readable directory containing relativePaths files
	 * @param relativePaths a set of paths relative to srcDir to readable File to copy
	 * @param destDir an existing, writable directory to copy files to
	 * @throws IllegalArgumentException if input invalid, IOException if operations fail
	 */
	public static File[] copyFiles(File srcDir, String[] relativePaths, File destDir) throws IllegalArgumentException, IOException {
		final String[] paths = relativePaths;
		throwIaxUnlessCanReadDir(srcDir, "srcDir");
		throwIaxUnlessCanWriteDir(destDir, "destDir");
		LangUtil.throwIaxIfNull(paths, "relativePaths");
		File[] result = new File[paths.length];
		for (int i = 0; i < paths.length; i++) {
			String path = paths[i];
			LangUtil.throwIaxIfNull(path, "relativePaths-entry");
			File src = newFile(srcDir, paths[i]);
			File dest = newFile(destDir, path);
			File destParent = dest.getParentFile();
			if (!destParent.exists()) {
				destParent.mkdirs();
			}
			LangUtil.throwIaxIfFalse(canWriteDir(destParent), "dest-entry-parent");
			copyFile(src, dest); // both file-dir and dir-dir copies
			result[i] = dest;
		}
		return result;
	}

	/**
	 * Copy fromFile to toFile, handling file-file, dir-dir, and file-dir copies.
	 *
	 * @param fromFile the File path of the file or directory to copy - must be readable
	 * @param toFile the File path of the target file or directory - must be writable (will be created if it does not exist)
	 */
	public static void copyFile(File fromFile, File toFile) throws IOException {
		LangUtil.throwIaxIfNull(fromFile, "fromFile");
		LangUtil.throwIaxIfNull(toFile, "toFile");
		LangUtil.throwIaxIfFalse(!toFile.equals(fromFile), "same file");
		if (toFile.isDirectory()) { // existing directory
			throwIaxUnlessCanWriteDir(toFile, "toFile");
			if (fromFile.isFile()) { // file-dir
				File targFile = new File(toFile, fromFile.getName());
				copyValidFiles(fromFile, targFile);
			} else if (fromFile.isDirectory()) { // dir-dir
				copyDir(fromFile, toFile);
			} else {
				LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
			}
		} else if (toFile.isFile()) { // target file exists
			if (fromFile.isDirectory()) {
				LangUtil.throwIaxIfFalse(false, "can't copy to file dir: " + fromFile);
			}
			copyValidFiles(fromFile, toFile); // file-file
		} else { // target file is a non-existent path -- could be file or dir
			/* File toFileParent = */ensureParentWritable(toFile);
			if (fromFile.isFile()) {
				copyValidFiles(fromFile, toFile);
			} else if (fromFile.isDirectory()) {
				toFile.mkdirs();
				throwIaxUnlessCanWriteDir(toFile, "toFile");
				copyDir(fromFile, toFile);
			} else {
				LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
			}
		}
	}

	/**
	 * Ensure that the parent directory to path can be written. If the path has a null parent, DEFAULT_PARENT is tested. If the path
	 * parent does not exist, this tries to create it.
	 *
	 * @param path the File path whose parent should be writable
	 * @return the File path of the writable parent directory
	 * @throws IllegalArgumentException if parent cannot be written or path is null.
	 */
	public static File ensureParentWritable(File path) {
		LangUtil.throwIaxIfNull(path, "path");
		File pathParent = path.getParentFile();
		if (null == pathParent) {
			pathParent = DEFAULT_PARENT;
		}
		if (!pathParent.canWrite()) {
			pathParent.mkdirs();
		}
		throwIaxUnlessCanWriteDir(pathParent, "pathParent");
		return pathParent;
	}

	/**
	 * Copy file to file.
	 *
	 * @param fromFile the File to copy (readable, non-null file)
	 * @param toFile the File to copy to (non-null, parent dir exists)
	 * @throws IOException
	 */
	public static void copyValidFiles(File fromFile, File toFile) throws IOException {
		FileInputStream in = null;
		FileOutputStream out = null;
		try {
			in = new FileInputStream(fromFile);
			out = new FileOutputStream(toFile);
			copyStream(in, out);
		} finally {
			if (out != null) {
				out.close();
			}
			if (in != null) {
				in.close();
			}
		}
	}

	/** do line-based copying */
	@SuppressWarnings("deprecation")
	public static void copyStream(DataInputStream in, PrintStream out) throws IOException {
		LangUtil.throwIaxIfNull(in, "in");
		LangUtil.throwIaxIfNull(in, "out");
		String s;
		while (null != (s = in.readLine())) {
			out.println(s);
		}
	}

	public static void copyStream(InputStream in, OutputStream out) throws IOException {
		final int MAX = 4096;
		byte[] buf = new byte[MAX];
		for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
			out.write(buf, 0, bytesRead);
		}
	}

	public static void copyStream(Reader in, Writer out) throws IOException {
		final int MAX = 4096;
		char[] buf = new char[MAX];
		for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
			out.write(buf, 0, bytesRead);
		}
	}

	/**
	 * Make a new child directory of parent
	 *
	 * @param parent a File for the parent (writable)
	 * @param child a prefix for the child directory
	 * @return a File dir that exists with parentDir as the parent file or null
	 */
	public static File makeNewChildDir(File parent, String child) {
		if (null == parent || !parent.canWrite() || !parent.isDirectory()) {
			throw new IllegalArgumentException("bad parent: " + parent);
		} else if (null == child) {
			child = "makeNewChildDir";
		} else if (!isValidFileName(child)) {
			throw new IllegalArgumentException("bad child: " + child);
		}
		File result = new File(parent, child);
		int safety = 1000;
		for (String suffix = FileUtil.randomFileString(); ((0 < --safety) && result.exists()); suffix = FileUtil.randomFileString()) {
			result = new File(parent, child + suffix);
		}
		if (result.exists()) {
			System.err.println("exhausted files for child dir in " + parent);
			return null;
		}
		return ((result.mkdirs() && result.exists()) ? result : null);
	}

	/**
	 * Make a new temporary directory in the same directory that the system uses for temporary files, or if that files, in the
	 * current directory.
	 *
	 * @param name the preferred (simple) name of the directory - may be null.
	 * @return File of an existing new temp dir, or null if unable to create
	 */
	public static File getTempDir(String name) {
		if (null == name) {
			name = "FileUtil_getTempDir";
		} else if (!isValidFileName(name)) {
			throw new IllegalArgumentException(" invalid: " + name);
		}
		File result = null;
		File tempFile = null;
		try {
			tempFile = File.createTempFile("ignoreMe", ".txt");
			File tempParent = tempFile.getParentFile();
			result = makeNewChildDir(tempParent, name);
		} catch (IOException t) {
			result = makeNewChildDir(new File("."), name);
		} finally {
			if (null != tempFile) {
				tempFile.delete();
			}
		}
		return result;
	}

	public static URL[] getFileURLs(File[] files) {
		if ((null == files) || (0 == files.length)) {
			return new URL[0];
		}
		URL[] result = new URL[files.length]; // XXX dangerous non-copy...
		for (int i = 0; i < result.length; i++) {
			result[i] = getFileURL(files[i]);
		}
		return result;
	}

	/**
	 * Get URL for a File. This appends "/" for directories. prints errors to System.err
	 *
	 * @param file the File to convert to URL (not null)
	 */
	@SuppressWarnings("deprecation")
	public static URL getFileURL(File file) {
		LangUtil.throwIaxIfNull(file, "file");
		URL result = null;
		try {
			result = file.toURL();// TODO AV - was toURI.toURL that does not
			// works on Java 1.3
			if (null != result) {
				return result;
			}
			String url = "file:" + file.getAbsolutePath().replace('\\', '/');
			result = new URL(url + (file.isDirectory() ? "/" : ""));
		} catch (MalformedURLException e) {
			String m = "Util.makeURL(\"" + file.getPath() + "\" MUE " + e.getMessage();
			System.err.println(m);
		}
		return result;
	}

	/**
	 * Write contents to file, returning null on success or error message otherwise. This tries to make any necessary parent
	 * directories first.
	 *
	 * @param file the File to write (not null)
	 * @param contents the String to write (use "" if null)
	 * @return String null on no error, error otherwise
	 */
	public static String writeAsString(File file, String contents) {
		LangUtil.throwIaxIfNull(file, "file");
		if (null == contents) {
			contents = "";
		}
		Writer out = null;
		try {
			File parentDir = file.getParentFile();
			if (!parentDir.exists() && !parentDir.mkdirs()) {
				return "unable to make parent dir for " + file;
			}
			Reader in = new StringReader(contents);
			out = new FileWriter(file);
			FileUtil.copyStream(in, out);
			return null;
		} catch (IOException e) {
			return LangUtil.unqualifiedClassName(e) + " writing " + file + ": " + e.getMessage();
		} finally {
			if (null != out) {
				try {
					out.close();
				} catch (IOException e) {
				} // ignored
			}
		}
	}

	/**
	 * Reads a boolean array with our encoding
	 */
	public static boolean[] readBooleanArray(DataInputStream s) throws IOException {
		int len = s.readInt();
		boolean[] ret = new boolean[len];
		for (int i = 0; i < len; i++) {
			ret[i] = s.readBoolean();
		}
		return ret;
	}

	/**
	 * Writes a boolean array with our encoding
	 */
	public static void writeBooleanArray(boolean[] a, DataOutputStream s) throws IOException {
		int len = a.length;
		s.writeInt(len);
		for (int i = 0; i < len; i++) {
			s.writeBoolean(a[i]);
		}
	}

	/**
	 * Reads an int array with our encoding
	 */
	public static int[] readIntArray(DataInputStream s) throws IOException {
		int len = s.readInt();
		int[] ret = new int[len];
		for (int i = 0; i < len; i++) {
			ret[i] = s.readInt();
		}
		return ret;
	}

	/**
	 * Writes an int array with our encoding
	 */
	public static void writeIntArray(int[] a, DataOutputStream s) throws IOException {
		int len = a.length;
		s.writeInt(len);
		for (int i = 0; i < len; i++) {
			s.writeInt(a[i]);
		}
	}

	/**
	 * Reads an int array with our encoding
	 */
	public static String[] readStringArray(DataInputStream s) throws IOException {
		int len = s.readInt();
		String[] ret = new String[len];
		for (int i = 0; i < len; i++) {
			ret[i] = s.readUTF();
		}
		return ret;
	}

	/**
	 * Writes an int array with our encoding
	 */
	public static void writeStringArray(String[] a, DataOutputStream s) throws IOException {
		if (a == null) {
			s.writeInt(0);
			return;
		}
		int len = a.length;
		s.writeInt(len);
		for (int i = 0; i < len; i++) {
			s.writeUTF(a[i]);
		}
	}

	/**
	 * Returns the contents of this file as a String
	 */
	public static String readAsString(File file) throws IOException {
		BufferedReader r = new BufferedReader(new FileReader(file));
		StringBuffer b = new StringBuffer();
		while (true) {
			int ch = r.read();
			if (ch == -1) {
				break;
			}
			b.append((char) ch);
		}
		r.close();
		return b.toString();
	}

	// /**
	// * Returns the contents of this stream as a String
	// */
	// public static String readAsString(InputStream in) throws IOException {
	// BufferedReader r = new BufferedReader(new InputStreamReader(in));
	// StringBuffer b = new StringBuffer();
	// while (true) {
	// int ch = r.read();
	// if (ch == -1)
	// break;
	// b.append((char) ch);
	// }
	// in.close();
	// r.close();
	// return b.toString();
	// }

	/**
	 * Returns the contents of this file as a byte[]
	 */
	public static byte[] readAsByteArray(File file) throws IOException {
		FileInputStream in = new FileInputStream(file);
		byte[] ret = FileUtil.readAsByteArray(in);
		in.close();
		return ret;
	}

	/**
	 * Reads this input stream and returns contents as a byte[]
	 */
	public static byte[] readAsByteArray(InputStream inStream) throws IOException {
		int size = 1024;
		byte[] ba = new byte[size];
		int readSoFar = 0;

		while (true) {
			int nRead = inStream.read(ba, readSoFar, size - readSoFar);
			if (nRead == -1) {
				break;
			}
			readSoFar += nRead;
			if (readSoFar == size) {
				int newSize = size * 2;
				byte[] newBa = new byte[newSize];
				System.arraycopy(ba, 0, newBa, 0, size);
				ba = newBa;
				size = newSize;
			}
		}

		byte[] newBa = new byte[readSoFar];
		System.arraycopy(ba, 0, newBa, 0, readSoFar);
		return newBa;
	}

	final static String FILECHARS = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

	/** @return semi-random String of length 6 usable as filename suffix */
	static String randomFileString() {
		final double FILECHARS_length = FILECHARS.length();
		final int LEN = 6;
		final char[] result = new char[LEN];
		int index = (int) (Math.random() * 6d);
		for (int i = 0; i < LEN; i++) {
			if (index >= LEN) {
				index = 0;
			}
			result[index++] = FILECHARS.charAt((int) (Math.random() * FILECHARS_length));
		}
		return new String(result);
	}

	public static InputStream getStreamFromZip(String zipFile, String name) {
		try {
			ZipFile zf = new ZipFile(zipFile);
			try {
				ZipEntry entry = zf.getEntry(name);
				return zf.getInputStream(entry);
			} finally {
				// ??? is it safe not to close this zf.close();
			}
		} catch (IOException ioe) {
			return null;
		}
	}

	//
	// public static void extractJar(String zipFile, String outDir) throws
	// IOException {
	// ZipInputStream zs = new ZipInputStream(new FileInputStream(zipFile));
	// ZipEntry entry;
	// while ((entry = zs.getNextEntry()) != null) {
	// if (entry.isDirectory())
	// continue;
	// byte[] in = readAsByteArray(zs);
	//
	// File outFile = new File(outDir + "/" + entry.getName());
	// // if (!outFile.getParentFile().exists())
	// // System.err.println("parent: " + outFile.getParentFile());
	// // System.err.println("parent: " + outFile.getParentFile());
	// outFile.getParentFile().mkdirs();
	// FileOutputStream os = new FileOutputStream(outFile);
	// os.write(in);
	// os.close();
	// zs.closeEntry();
	// }
	// zs.close();
	// }

	/**
	 * Do line-based search for literal text in source files, returning file:line where found.
	 *
	 * @param sought the String text to seek in the file
	 * @param sources the List of String paths to the source files
	 * @param listAll if false, only list first match in file
	 * @param errorSink the PrintStream to print any errors to (one per line) (use null to silently ignore errors)
	 * @return List of String of the form file:line for each found entry (never null, might be empty)
	 */
	// OPTIMIZE only used by tests? move it out
	public static List lineSeek(String sought, List sources, boolean listAll, PrintStream errorSink) {
		if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sources)) {
			return Collections.emptyList();
		}
		ArrayList result = new ArrayList();
		for (Iterator iter = sources.iterator(); iter.hasNext();) {
			String path = iter.next();
			String error = lineSeek(sought, path, listAll, result);
			if ((null != error) && (null != errorSink)) {
				errorSink.println(error);
			}
		}
		return result;
	}

	/**
	 * Do line-based search for literal text in source file, returning line where found as a String in the form
	 * {sourcePath}:line:column submitted to the collecting parameter sink. Any error is rendered to String and returned as the
	 * result.
	 *
	 * @param sought the String text to seek in the file
	 * @param sources the List of String paths to the source files
	 * @param listAll if false, only list first match in file
	 * @param List sink the List for String entries of the form {sourcePath}:line:column
	 * @return String error if any, or add String entries to sink
	 */
	public static String lineSeek(String sought, String sourcePath, boolean listAll, ArrayList sink) {
		if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sourcePath)) {
			return "nothing sought";
		}
		if (LangUtil.isEmpty(sourcePath)) {
			return "no sourcePath";
		}
		final File file = new File(sourcePath);
		if (!file.canRead() || !file.isFile()) {
			return "sourcePath not a readable file";
		}
		int lineNum = 0;
		FileReader fin = null;
		try {
			fin = new FileReader(file);
			BufferedReader reader = new BufferedReader(fin);
			String line;
			while (null != (line = reader.readLine())) {
				lineNum++;
				int loc = line.indexOf(sought);
				if (-1 != loc) {
					sink.add(sourcePath + ":" + lineNum + ":" + loc);
					if (!listAll) {
						break;
					}
				}
			}
		} catch (IOException e) {
			return LangUtil.unqualifiedClassName(e) + " reading " + sourcePath + ":" + lineNum;
		} finally {
			try {
				if (null != fin) {
					fin.close();
				}
			} catch (IOException e) {
			} // ignore
		}
		return null;
	}

	public static BufferedOutputStream makeOutputStream(File file) throws FileNotFoundException {
		File parent = file.getParentFile();
		if (parent != null) {
			parent.mkdirs();
		}
		return new BufferedOutputStream(new FileOutputStream(file));
	}

	/**
	 * Sleep until after the last last-modified stamp from the files.
	 *
	 * @param files the File[] of files to inspect for last modified times (this ignores null or empty files array and null or
	 *        non-existing components of files array)
	 * @return true if succeeded without 100 interrupts
	 */
	public static boolean sleepPastFinalModifiedTime(File[] files) {
		if ((null == files) || (0 == files.length)) {
			return true;
		}
		long delayUntil = System.currentTimeMillis();
		for (int i = 0; i < files.length; i++) {
			File file = files[i];
			if ((null == file) || !file.exists()) {
				continue;
			}
			long nextModTime = file.lastModified();
			if (nextModTime > delayUntil) {
				delayUntil = nextModTime;
			}
		}
		return LangUtil.sleepUntil(++delayUntil);
	}

	private static void listClassFiles(final File baseDir, ArrayList result) {
		File[] files = baseDir.listFiles();
		for (int i = 0; i < files.length; i++) {
			File f = files[i];
			if (f.isDirectory()) {
				listClassFiles(f, result);
			} else {
				if (f.getName().endsWith(".class")) {
					result.add(f);
				}
			}
		}
	}

	private static void listFiles(final File baseDir, ArrayList result, FileFilter filter) {
		File[] files = baseDir.listFiles();
		// hack https://bugs.eclipse.org/bugs/show_bug.cgi?id=48650
		final boolean skipCVS = (!PERMIT_CVS && (filter == aspectjSourceFileFilter));
		for (int i = 0; i < files.length; i++) {
			File f = files[i];
			if (f.isDirectory()) {
				if (skipCVS) {
					String name = f.getName().toLowerCase();
					if ("cvs".equals(name) || "sccs".equals(name)) {
						continue;
					}
				}
				listFiles(f, result, filter);
			} else {
				if (filter.accept(f)) {
					result.add(f);
				}
			}
		}
	}

	/** @return true if input is not null and contains no path separator */
	private static boolean isValidFileName(String input) {
		return ((null != input) && (-1 == input.indexOf(File.pathSeparator)));
	}

	private static void listFiles(final File baseDir, String dir, ArrayList result) {
		final String dirPrefix = (null == dir ? "" : dir + "/");
		final File dirFile = (null == dir ? baseDir : new File(baseDir.getPath() + "/" + dir));
		final String[] files = dirFile.list();
		for (int i = 0; i < files.length; i++) {
			File f = new File(dirFile, files[i]);
			String path = dirPrefix + files[i];
			if (f.isDirectory()) {
				listFiles(baseDir, path, result);
			} else {
				result.add(path);
			}
		}
	}

	private FileUtil() {
	}

	public static List makeClasspath(URL[] urls) {
		List ret = new LinkedList();
		if (urls != null) {
			for (int i = 0; i < urls.length; i++) {
				ret.add(toPathString(urls[i]));
			}
		}
		return ret;
	}

	private static String toPathString(URL url) {
		try {
			return url.toURI().getPath();
		} catch (URISyntaxException e) {
			System.err.println("Warning!! Malformed URL may cause problems: "+url); // TODO: Better way to report this?
			// In this case it was likely not using properly escaped
			// characters so we just use the 'bad' method that doesn't decode
			// special chars
			return url.getPath();
		}
	}

	/**
	 * A pipe when run reads from an input stream to an output stream, optionally sleeping between reads.
	 *
	 * @see #copyStream(InputStream, OutputStream)
	 */
	public static class Pipe implements Runnable {
		private final InputStream in;
		private final OutputStream out;
		private final long sleep;
		private ByteArrayOutputStream snoop;
		private long totalWritten;
		private Throwable thrown;
		private boolean halt;
		/**
		 * Seem to be unable to detect erroroneous closing of System.out...
		 */
		private final boolean closeInput;
		private final boolean closeOutput;

		/**
		 * If true, then continue processing stream until no characters are returned when halting.
		 */
		private boolean finishStream;

		private boolean done; // true after completing() completes

		/**
		 * alias for Pipe(in, out, 100l, false, false)
		 *
		 * @param in the InputStream source to read
		 * @param out the OutputStream sink to write
		 */
		Pipe(InputStream in, OutputStream out) {
			this(in, out, 100l, false, false);
		}

		/**
		 * @param in the InputStream source to read
		 * @param out the OutputStream sink to write
		 * @param tryClosingStreams if true, then try closing both streams when done
		 * @param sleep milliseconds to delay between reads (pinned to 0..1 minute)
		 */
		Pipe(InputStream in, OutputStream out, long sleep, boolean closeInput, boolean closeOutput) {
			LangUtil.throwIaxIfNull(in, "in");
			LangUtil.throwIaxIfNull(out, "out");
			this.in = in;
			this.out = out;
			this.closeInput = closeInput;
			this.closeOutput = closeOutput;
			this.sleep = Math.min(0l, Math.max(60l * 1000l, sleep));
		}

		public void setSnoop(ByteArrayOutputStream snoop) {
			this.snoop = snoop;
		}

		/**
		 * Run the pipe. This halts on the first Throwable thrown or when a read returns -1 (for end-of-file) or on demand.
		 */
		public void run() {
			totalWritten = 0;
			if (halt) {
				return;
			}
			try {
				final int MAX = 4096;
				byte[] buf = new byte[MAX];
				// TODO this blocks, hanging the harness
				int count = in.read(buf, 0, MAX);
				ByteArrayOutputStream mySnoop;
				while ((halt && finishStream && (0 < count)) || (!halt && (-1 != count))) {
					out.write(buf, 0, count);
					mySnoop = snoop;
					if (null != mySnoop) {
						mySnoop.write(buf, 0, count);
					}
					totalWritten += count;
					if (halt && !finishStream) {
						break;
					}
					if (!halt && (0 < sleep)) {
						Thread.sleep(sleep);
					}
					if (halt && !finishStream) {
						break;
					}
					count = in.read(buf, 0, MAX);
				}
			} catch (Throwable e) {
				thrown = e;
			} finally {
				halt = true;
				if (closeInput) {
					try {
						in.close();
					} catch (IOException e) {
						// ignore
					}
				}
				if (closeOutput) {
					try {
						out.close();
					} catch (IOException e) {
						// ignore
					}
				}
				done = true;
				completing(totalWritten, thrown);
			}

		}

		/**
		 * Tell the pipe to halt the next time it gains control.
		 *
		 * @param wait if true, this waits synchronously until pipe is done
		 * @param finishStream if true, then continue until a read from the input stream returns no bytes, then halt.
		 * @return true if run() will return the next time it gains control
		 */
		public boolean halt(boolean wait, boolean finishStream) {
			if (!halt) {
				halt = true;
			}
			if (wait) {
				while (!done) {
					synchronized (this) {
						notifyAll();
					}
					if (!done) {
						try {
							Thread.sleep(5l);
						} catch (InterruptedException e) {
							break;
						}
					}
				}
			}
			return halt;
		}

		/** @return the total number of bytes written */
		public long totalWritten() {
			return totalWritten;
		}

		/** @return any exception thrown when reading/writing */
		public Throwable getThrown() {
			return thrown;
		}

		/**
		 * This is called when the pipe is completing. This implementation does nothing. Subclasses implement this to get notice.
		 * Note that halt(true, true) might or might not have completed before this method is called.
		 */
		protected void completing(long totalWritten, Throwable thrown) {
		}
	}

}




© 2018 Weber Informatics LLC