blob: 20ce455f0a20891ea9b0f64d1979a126a3068872 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 The University of York.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Dimitrios Kolovos - initial API and implementation
******************************************************************************/
package org.eclipse.epsilon.common.util;
import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE;
import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;
import static java.security.AccessController.doPrivileged;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.channels.FileChannel;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FileUtil {
private static Logger logger = LoggerFactory.getLogger(FileUtil.class);
// temporary directory location
private static final Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir"));
private static final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix");
// default file and directory permissions (lazily initialized)
private static class PosixPermissions {
static final FileAttribute<Set<PosixFilePermission>> filePermissions = PosixFilePermissions
.asFileAttribute(EnumSet.of(OWNER_READ, OWNER_WRITE));
static final FileAttribute<Set<PosixFilePermission>> dirPermissions = PosixFilePermissions
.asFileAttribute(EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
}
private FileUtil() {
}
public static Path getCurrentDirectory() {
return Paths.get(".").toAbsolutePath().normalize();
}
public static void setFileContents(String str, File file) throws Exception {
try (FileWriter writer = new FileWriter(file)) {
writer.append(str);
writer.flush();
}
}
public static String replaceExtension(String filename, String newExtension) {
int dotIndex = filename.lastIndexOf('.');
if (dotIndex > -1) {
filename = filename.substring(0, dotIndex + 1) + newExtension;
}
return filename;
}
public static String removeExtension(String filename) {
int dotIndex = filename.lastIndexOf('.');
if (dotIndex > -1) {
filename = filename.substring(0, dotIndex);
}
return filename;
}
public static String getFileName(String path) {
return getFileName(path, true);
}
public static String getFileName(String path, boolean includeExtension) {
String filename = path.substring(path.replace("\\", "/").lastIndexOf('/') + 1);
if (!includeExtension) {
filename = removeExtension(filename);
}
return filename;
}
/**
* Copied from @linkplain{https://stackoverflow.com/a/3571239/5870336}
*
* @param filename
* @return
* @since 1.6
*/
public static String getExtension(String filename) {
String extension = "";
int dotIndex = filename.lastIndexOf('.');
if (dotIndex > Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'))) {
extension = filename.substring(dotIndex + 1);
}
return extension;
}
public static String getFileContents(File file) throws Exception {
final StringBuffer buffer = new StringBuffer();
final String lineSeparator = System.getProperty("line.separator");
for (String line : getFileLineContents(file)) {
buffer.append(line);
buffer.append(lineSeparator);
}
return buffer.toString();
}
public static Collection<String> getFileLineContents(File file) throws Exception {
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
final List<String> lines = new LinkedList<>();
String line = bufferedReader.readLine();
while (line != null) {
lines.add(line);
line = bufferedReader.readLine();
}
return lines;
}
}
public static String getAbsolutePath(String basePath, String relativePath) {
File file = new File(relativePath);
if (!file.isAbsolute()) {
file = new File(basePath, relativePath);
}
return file.getAbsolutePath();
}
/**
* Gets a file stored as a resource in a jar. Since not all users of the file
* can read from inside jars, we get the file as a stream and create a temp file
* with its contents.
*
* @param name
* @param relativeTo
* @return
*/
public static File getFile(String name, Class<?> relativeTo) {
logger.info("getFile {}@{}", name, relativeTo.getSimpleName());
InputStream is = relativeTo.getResourceAsStream(name);
return inputStreamToFile(is, name);
}
@Deprecated
public static File getDirectoryOf(Class<?> clazz) {
return getFile(clazz.getSimpleName() + ".class", clazz).getParentFile();
}
@Deprecated
public static String getPath(String name, Class<?> relativeTo) {
return getFile(name, relativeTo).getAbsolutePath();
}
public static void checkFileExists(final File file) throws FileNotFoundException {
if (!file.exists()) {
throw new FileNotFoundException("File " + file.getPath() + " does not exist");
}
}
public static File createTempFile(String name) {
return createTempFile(name, "tmp");
}
public static File createTempFile(String name, String extension) {
Path tmpFile;
try {
// tmpFile = Files.createTempFile(name, extension);
tmpFile = createTempFile(null, name, extension);
} catch (IOException e) {
throw new IllegalArgumentException("Could not create temp file ", e);
}
return tmpFile.toFile();
}
public static File createTempDir(String name) {
return createTempDir(name, false);
}
public static File createTempDir(String name, boolean reuse) {
Path tmpFile = null;
if (reuse) {
DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
@Override
public boolean accept(Path file) throws IOException {
return (Files.isDirectory(file) && file.getFileName().startsWith(Paths.get(name)));
}
};
Iterator<Path> it;
try {
it = Files.newDirectoryStream(tmpdir, filter).iterator();
tmpFile = it.hasNext() ? it.next() : null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (tmpFile == null) {
try {
tmpFile = createTempDirectory(null, name);
} catch (IOException e) {
throw new IllegalArgumentException("Could not create temp directory ", e);
}
}
return tmpFile.toFile();
}
public static File copyToTemp(File srcFile) throws IOException {
File tmpFile = File.createTempFile("filecompare", "tmp");
if (srcFile.isDirectory()) {
tmpFile.delete();
tmpFile.mkdir();
}
copy(srcFile, tmpFile);
return tmpFile;
}
public static void copy(File srcFile, File dstFile) throws IOException {
if (srcFile.isDirectory()) {
dstFile.mkdir();
for (File entry : srcFile.listFiles()) {
copy(entry, new File(dstFile, entry.getName()));
}
} else {
// Based on the second answer in http://stackoverflow.com/questions/106770.
FileInputStream isSrc = null;
FileOutputStream osDst = null;
FileChannel chSrc = null;
FileChannel chDst = null;
try {
isSrc = new FileInputStream(srcFile);
osDst = new FileOutputStream(dstFile);
chSrc = isSrc.getChannel();
chDst = osDst.getChannel();
final long srcBytes = srcFile.length();
long transferred = 0;
while (transferred < srcBytes) {
transferred += chDst.transferFrom(chSrc, transferred, srcBytes);
chDst.position(transferred);
}
} finally {
if (chDst != null) {
chDst.close();
} else if (osDst != null) {
osDst.close();
}
if (chSrc != null) {
chSrc.close();
} else if (isSrc != null) {
isSrc.close();
}
}
}
}
public static Set<String> listFilesAsSet(File fileExpected) {
return new HashSet<>(Arrays.asList(fileExpected.list()));
}
/**
* We implement our own comparison algorithm here, so we don't need Eclipse
* Compare to compute differences, but rather only to show them in the UI.
*/
public static boolean sameContents(File fileExpected, File fileActual, Set<String> ignoreFilenames)
throws IOException {
if (fileExpected.isDirectory() != fileActual.isDirectory()) {
// One is a file, the other is a directory: not the same
return false;
}
if (fileExpected.isDirectory()) {
// Both are directories: they should contain the same filenames,
// and each pair should have the same contents
final Set<String> expectedFilenames = listFilesAsSet(fileExpected);
final Set<String> actualFilenames = listFilesAsSet(fileActual);
expectedFilenames.removeAll(ignoreFilenames);
actualFilenames.removeAll(ignoreFilenames);
if (!expectedFilenames.equals(actualFilenames)) {
return false;
}
for (String filename : expectedFilenames) {
final File expectedEntry = new File(fileExpected, filename);
final File actualEntry = new File(fileActual, filename);
if (!sameContents(expectedEntry, actualEntry, ignoreFilenames)) {
return false;
}
}
return true;
} else {
if (fileExpected.length() != fileActual.length()) {
// Different length: no need to read the files
return false;
}
try (FileInputStream isExpected = new FileInputStream(fileExpected)) {
try (FileInputStream isActual = new FileInputStream(fileActual)) {
return sameContents(isExpected, isActual);
}
}
}
}
public static boolean sameContents(InputStream isExpected, InputStream isActual) throws IOException {
int chExpected, chActual;
do {
chExpected = isExpected.read();
chActual = isActual.read();
} while (chExpected == chActual && chExpected > 0 && chActual > 0);
return chExpected == chActual;
}
/**
* WARNIING: Use with caution! Deletes all contents and sub-directories of the
* specified path.
*
* @param dir The absolute path to the directory.
* @throws IOException
* @since 1.6
*/
public static void deleteDirectory(String dir) throws IOException {
Path path = Paths.get(dir);
if (Files.exists(path)) {
Files.walk(path).map(Path::toFile).sorted((o1, o2) -> -o1.compareTo(o2)).forEach(File::delete);
}
}
/**
* Reads entire directory recursively, mapping the contents of each file as a
* string to its path.
*
* @param dir The root directory.
* @return The contents of each file in the directory and its subdirectories.
* @throws IOException
* @since 1.6
*/
public static Map<Path, String> readDirectory(String dir) throws IOException {
Map<Path, String> contents = new HashMap<>();
for (Path path : ((Iterable<Path>) Files.walk(Paths.get(dir))::iterator)) {
if (Files.isRegularFile(path)) {
contents.put(path, new String(Files.readAllBytes(path)));
}
}
return contents;
}
private static File inputStreamToFile(InputStream inputStream, String name) {
logger.debug("inputStreamToFile {}", name);
OutputStream outputStream = null;
String prefix = name;
String suffix = "";
Path dirStructure = null;
// Name might be a path, get the file name, remove it
if (name.contains("/")) {
dirStructure = Paths.get(tmpdir.toString(), name.substring(0, name.lastIndexOf("/")), "/");
dirStructure.toFile().mkdirs();
name = name.substring(name.lastIndexOf("/") + 1);
logger.debug("inputStreamToFile {}", name);
}
String[] parts = name.split("\\.");
if (parts.length == 2) {
prefix = parts[0];
suffix = "." + parts[1];
logger.debug("splitting name and extension: {} - {}", prefix, suffix);
}
File file = null;
try {
Path tempFile;
try {
// tempFile = Files.createTempFile(prefix, sufix);
tempFile = createTempFile(dirStructure, prefix, suffix);
} catch (IllegalArgumentException e) {
logger.error("Error creating temp file: {}-{}", prefix, suffix, e);
throw e;
}
outputStream = new FileOutputStream(tempFile.toFile());
int read = 0;
byte[] bytes = new byte[1024];
logger.debug("{} bytes available", inputStream.available());
while ((read = inputStream.read(bytes)) != -1) {
logger.debug("{} bytes read from input", read);
outputStream.write(bytes, 0, read);
}
logger.debug("Copying data into temp file Done!");
// Runtime.getRuntime().addShutdownHook(new Thread() {
// @Override
// public void run() {
// try {
// Files.delete(tempFile);
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// });
file = tempFile.toFile();
} catch (IOException e) {
logger.error("Unable to create File for resoruce", e);
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}
static Path createTempDirectory(Path dir, String prefix, FileAttribute<?>... attrs) throws IOException {
return create(dir, prefix, null, true, attrs);
}
static Path createTempFile(Path dir, String prefix, String suffix, FileAttribute<?>... attrs) throws IOException {
return create(dir, prefix, suffix, false, attrs);
}
/**
* Creates a file or directory in in the given given directory (or in the
* temporary directory if dir is {@code null}).
*/
private static Path create(Path dir, String prefix, String suffix, boolean createDirectory,
FileAttribute<?>[] attrs) throws IOException {
if (prefix == null)
prefix = "";
if (suffix == null)
suffix = (createDirectory) ? "" : ".tmp";
if (dir == null)
dir = tmpdir;
// in POSIX environments use default file and directory permissions
// if initial permissions not given by caller.
if (isPosix && (dir.getFileSystem() == FileSystems.getDefault())) {
if (attrs.length == 0) {
// no attributes so use default permissions
attrs = new FileAttribute<?>[1];
attrs[0] = (createDirectory) ? PosixPermissions.dirPermissions : PosixPermissions.filePermissions;
} else {
// check if posix permissions given; if not use default
boolean hasPermissions = false;
for (int i = 0; i < attrs.length; i++) {
if (attrs[i].name().equals("posix:permissions")) {
hasPermissions = true;
break;
}
}
if (!hasPermissions) {
FileAttribute<?>[] copy = new FileAttribute<?>[attrs.length + 1];
System.arraycopy(attrs, 0, copy, 0, attrs.length);
attrs = copy;
attrs[attrs.length - 1] = (createDirectory) ? PosixPermissions.dirPermissions
: PosixPermissions.filePermissions;
}
}
}
// loop generating random names until file or directory can be created
SecurityManager sm = System.getSecurityManager();
//for (;;) {
Path f;
try {
f = generatePath(prefix, suffix, dir);
} catch (InvalidPathException e) {
// don't reveal temporary directory location
if (sm != null)
throw new IllegalArgumentException("Invalid prefix or suffix");
throw e;
}
try {
if (createDirectory) {
return Files.createDirectory(f, attrs);
} else {
return Files.createFile(f, attrs);
}
} catch (SecurityException e) {
// don't reveal temporary directory location
if (dir == tmpdir && sm != null)
throw new SecurityException("Unable to create temporary file or directory");
throw e;
} catch (FileAlreadyExistsException e) {
// FIXME Add the file deletion so we dont need this.
// FIXME or make each test use a different folder...
logger.debug("File found, reusing");
return f;
}
//}
}
private static Path generatePath(String prefix, String suffix, Path dir) {
Path name = dir.getFileSystem().getPath(prefix + suffix);
// the generated name should be a simple file name
if (name.getParent() != null)
throw new IllegalArgumentException("Invalid prefix or suffix");
return dir.resolve(name);
}
}