/*******************************************************************************
* Copyright (c) 2014 Mentor Graphics and others.
* 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:
* Mentor Graphics - initial API and implementation
*******************************************************************************/
package com.codesourcery.internal.installer;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.CopyOption;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.osgi.util.NLS;
import com.codesourcery.installer.Installer;
/**
* Common file utility helper methods.
*/
public class FileUtils {
/** New line separator */
private static final String NEWLINE = System.getProperty("line.separator");
/**
* Copies source directory to target and preserves attributes. Any existing files will be replaced.
* Links will be followed.
*
* @param source Path of source directory
* @param target Path of target directory
* @param replace <code>true</code> to replace already existing target directory
* @throws IOException on failure
*/
public static void copyDirectory(Path source, Path target, boolean replace) throws IOException {
CopyOption[] options = (replace)?new CopyOption[] {StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING}:
new CopyOption[] {StandardCopyOption.COPY_ATTRIBUTES};
// Follow links during copy.
EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
FilesCopier tc = new FilesCopier(source, target, options);
Files.walkFileTree(source, opts, Integer.MAX_VALUE, tc);
}
/**
* Copies source file to destination by preserving file attributes
* @param srcFile Path of source file.
* @param destFile Path of target file.
* @param replace <code>true</code> to replace already existing target file.
* @throws IOException in case of failure
*/
public static void copyFile(Path srcFile, Path destFile, boolean replace) throws IOException {
CopyOption[] options = (replace)?new CopyOption[] {StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING}:
new CopyOption[] {StandardCopyOption.COPY_ATTRIBUTES};
Files.copy(srcFile, destFile, options);
}
/**
* Deletes a directory.
*
* @param directory Directory to delete
* @throws IOException on failure to delete the directory
*/
public static void deleteDirectory(Path directory) throws IOException {
File[] files = deleteFiles(directory, null, new NullProgressMonitor());
if (files.length > 0) {
throw new IOException("Failed to delete " + files[0].getAbsolutePath() + ".");
}
else {
Files.delete(directory);
}
}
/**
* Deletes files in a directory. This method does not delete the directory itself.
* This method does not throw an exception on failure, but instead returns any files that could not be deleted.
*
* @param directory Directory to delete
* @param excludedPaths Paths (files or directories) to exclude from deletion or <code>null</code>
* @param monitor Progress monitor or <code>null</code>
* @return Files that could not be deleted
*/
public static File[] deleteFiles(final Path directory, final Path[] excludedPaths, final IProgressMonitor monitor) {
final ArrayList<File> filesNotRemoved = new ArrayList<File>();
final IPath directoryPath = new org.eclipse.core.runtime.Path(directory.toFile().getAbsolutePath());
try {
final int[] fileCount = new int[] { 0 };
// Count file to be removed
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path,
BasicFileAttributes attrs) throws IOException {
fileCount[0] ++;
// If file is read-only, make it writable
File file = path.toFile();
if (!file.canWrite()) {
if (!file.setWritable(true)) {
throw new IOException("Failed to set file writeable: " + file.getAbsolutePath());
}
}
return FileVisitResult.CONTINUE;
}
});
if (monitor != null) {
monitor.beginTask(NLS.bind(InstallMessages.Removing0, ""), fileCount[0]);
}
// Delete files and sub-directories
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
/**
* Returns if a path is excluded.
*
* @param arg Path
* @return <code>true</code> if path is excluded
*/
private boolean isExcluded(Path arg) {
boolean excluded = false;
if (excludedPaths != null) {
for (Path excludedPath : excludedPaths) {
if (excludedPath.equals(arg)) {
excluded = true;
break;
}
}
}
return excluded;
}
@Override
public FileVisitResult preVisitDirectory(Path arg0,
BasicFileAttributes arg1) throws IOException {
// Is directory excluded
if (isExcluded(arg0)) {
return FileVisitResult.SKIP_SUBTREE;
}
else {
return FileVisitResult.CONTINUE;
}
}
@Override
public FileVisitResult postVisitDirectory(Path arg0,
IOException arg1) throws IOException {
try {
if (!arg0.equals(directory)) {
if (monitor != null) {
IPath filePath = new org.eclipse.core.runtime.Path(arg0.toFile().getAbsolutePath()).removeFirstSegments(directoryPath.segmentCount()).setDevice("");
monitor.setTaskName(NLS.bind(InstallMessages.Removing0, filePath.toOSString()));
}
// Delete directory
Files.delete(arg0);
}
}
catch (Exception e) {
// Ignore
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path arg0, BasicFileAttributes arg1)
throws IOException {
try {
if (!isExcluded(arg0)) {
// Delete file
Files.delete(arg0);
}
}
catch (Exception e) {
filesNotRemoved.add(arg0.toFile());
Installer.log(e);
}
if (monitor != null) {
monitor.worked(1);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
// If an exception is thrown from visitor, it is an error
Installer.log(e);
} finally {
if (monitor != null) {
monitor.done();
}
}
return filesNotRemoved.toArray(new File[filesNotRemoved.size()]);
}
/**
* Reads the entire contents of a file into a string.
*
* @param file File to read
* @return File contents
* @throws IOException on failure to read file
*/
public static String readFile(File file) throws IOException {
StringBuilder buffer = new StringBuilder();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
String line = null;
while((line = reader.readLine()) != null) {
buffer.append(line);
buffer.append(NEWLINE);
}
}
finally {
if (reader != null) {
reader.close();
}
}
return buffer.toString();
}
/**
* Sets all files in a directory to be writable.
*
* @param directory Directory
* @throws IOException on failure
*/
public static void setWritable(Path directory) throws IOException {
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
/**
* Sets a directory or file writable.
*
* @param path Path to directory or file
* @return File visit result
* @throws IOException on failure
*/
private FileVisitResult setWritable(Path path) throws IOException {
File file = path.toFile();
if (!file.canWrite()) {
if (!file.setWritable(true)) {
throw new IOException("Failed to set file writeable: " + file.getAbsolutePath());
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
return setWritable(dir);
}
@Override
public FileVisitResult visitFile(Path arg0, BasicFileAttributes arg1)
throws IOException {
return setWritable(arg0);
}
});
}
}