blob: 93908b7baa86fa7695a1c5afa2526a7234f84b27 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Martin Karpisek <martin.karpisek@gmail.com> - Bug 525701
*******************************************************************************/
package org.eclipse.pde.api.tools.tests.util;
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.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.pde.api.tools.internal.util.Util;
import org.eclipse.pde.api.tools.model.tests.TestSuiteHelper;
/**
* Utilities for handling files
*
* @since 1.0.0
*/
public class FileUtils {
/**
* Maximum of time in ms to wait in deletion operation while running
* JDT/Core tests. Default is 10 seconds. This number cannot exceed 1 minute
* (i.e. 60000). <br>
* To avoid too many loops while waiting, the ten first ones are done
* waiting 10ms before repeating, the ten loops after are done waiting 100ms
* and the other loops are done waiting 1s...
*/
public static int DELETE_MAX_WAIT = 10000;
/**
* Time wasted deleted resources
*/
private static int DELETE_MAX_TIME = 0;
/**
* Recursively adds files from the specified directory to the provided list
*
* @param dir
* @param collection
* @throws IOException
*/
public static void addJavaFiles(File dir, List<File> collection) throws IOException {
File[] files = dir.listFiles();
List<File> subDirs = new ArrayList<>(2);
for (int i = 0; i < files.length; i++) {
if (files[i].isFile()) {
collection.add(files[i]);
} else if (files[i].isDirectory()) {
subDirs.add(files[i]);
}
}
Iterator<File> iter = subDirs.iterator();
while (iter.hasNext()) {
File subDir = iter.next();
addJavaFiles(subDir, collection);
}
}
/**
* Imports files from the specified root directory into the specified path
*
* @param rootDir
* @param destPath
* @param monitor
* @throws InvocationTargetException
* @throws IOException
*/
public static void importFilesFromDirectory(File rootDir, IPath destPath, IProgressMonitor monitor) throws InvocationTargetException, IOException {
IResource findMember = ResourcesPlugin.getWorkspace().getRoot().getFolder(destPath);
File dest = findMember.getLocation().toFile();
if (!dest.exists()) {
dest.mkdirs();
}
TestSuiteHelper.copy(rootDir, dest);
try {
findMember.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e) {
}
}
/**
* Imports the specified file to the destination path
*
* @param file
* @param destPath
* @param monitor
* @throws InvocationTargetException
* @throws IOException
*/
public static void importFileFromDirectory(File file, IPath destPath, IProgressMonitor monitor) throws InvocationTargetException, IOException {
IResource findMember = null;
if (destPath.segmentCount() == 1) {
findMember = ResourcesPlugin.getWorkspace().getRoot().getProject(destPath.lastSegment());
} else {
findMember = ResourcesPlugin.getWorkspace().getRoot().getFolder(destPath);
}
if (findMember == null) {
return;
}
File dest = findMember.getLocation().toFile();
if (!dest.exists()) {
dest.mkdirs();
}
TestSuiteHelper.copy(file, dest);
try {
findMember.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e) {
}
}
/**
* Creates a new java.io.File at the given path with the given contents
*
* @param path
* @param contents
* @throws IOException
*/
public static void createFile(String path, String contents) throws IOException {
try (FileOutputStream output = new FileOutputStream(path)) {
output.write(contents.getBytes());
}
}
/**
* Delete a file or directory and insure that the file is no longer present
* on file system. In case of directory, delete all the hierarchy
* underneath.
*
* @param resource The resource to delete
* @return true iff the file was really delete, false otherwise
*/
public static boolean delete(IResource resource) {
try {
resource.delete(true, null);
if (isResourceDeleted(resource)) {
return true;
}
} catch (CoreException e) {
// skip
}
return waitUntilResourceDeleted(resource);
}
/**
* Delete a file or directory and insure that the file is no longer present
* on file system. In case of directory, delete all the hierarchy
* underneath.
*
* @param path The path of the file or directory to delete
* @return true iff the file was really delete, false otherwise
*/
public static boolean delete(String path) {
return Util.delete(new File(path));
}
/**
* Flush content of a given directory (leaving it empty), no-op if not a
* directory.
*/
public static void flushDirectoryContent(File dir) {
File[] files = dir.listFiles();
if (files == null) {
return;
}
for (int i = 0, max = files.length; i < max; i++) {
Util.delete(files[i]);
}
}
/**
* Wait until a resource is _really_ deleted on file system.
*
* @param resource Deleted resource
* @return true if the file was finally deleted, false otherwise
*/
private static boolean waitUntilResourceDeleted(IResource resource) {
IPath location = resource.getLocation();
if (location == null) {
System.out.println();
System.out.println(" !!! ERROR: " + resource + " getLocation() returned null!!!"); //$NON-NLS-1$ //$NON-NLS-2$
System.out.println();
return false;
}
File file = location.toFile();
int count = 0;
int delay = 10; // ms
int maxRetry = DELETE_MAX_WAIT / delay;
int time = 0;
while (count < maxRetry) {
try {
count++;
Thread.sleep(delay);
time += delay;
if (time > DELETE_MAX_TIME) {
DELETE_MAX_TIME = time;
}
if (resource.isAccessible()) {
try {
resource.delete(true, null);
if (isResourceDeleted(resource) && Util.isFileDeleted(file)) {
return true;
}
} catch (CoreException e) {
}
}
if (isResourceDeleted(resource) && Util.isFileDeleted(file)) {
return true;
}
// Increment waiting delay exponentially
if (count >= 10 && delay <= 100) {
count = 1;
delay *= 10;
maxRetry = DELETE_MAX_WAIT / delay;
if ((DELETE_MAX_WAIT % delay) != 0) {
maxRetry++;
}
}
} catch (InterruptedException ie) {
break; // end loop
}
}
System.out.println();
System.out.println(" !!! ERROR: " + resource + " was never deleted even after having waited " + DELETE_MAX_TIME + "ms!!!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
System.out.println();
return false;
}
/**
* Returns whether a resource is really deleted or not. Does not only rely
* on {@link IResource#isAccessible()} method but also look if it's not in
* its parent children {@link #getParentChildResource(IResource)}.
*
* @param resource The resource to test if deleted
* @return true if the resource is not accessible and was not found in its
* parent children.
*/
public static boolean isResourceDeleted(IResource resource) {
return !resource.isAccessible() && getParentChildResource(resource) == null;
}
/**
* Reads the content of the given source file.
*
* Returns null if unable to read given source file.
*/
public static String readFromFile(String sourceFilePath) throws FileNotFoundException, IOException {
File sourceFile = new File(sourceFilePath);
if (!sourceFile.exists()) {
return null;
}
if (!sourceFile.isFile()) {
return null;
}
StringBuilder sourceContentBuffer = new StringBuilder();
try (FileInputStream input = new FileInputStream(sourceFile)) {
int read;
do {
read = input.read();
if (read != -1) {
sourceContentBuffer.append((char) read);
}
} while (read != -1);
input.close();
}
return sourceContentBuffer.toString();
}
/**
* Writes the given content string to the output file, specified
*
* @param contents
* @param destinationFilePath
*/
public static void writeToFile(String contents, String destinationFilePath) {
File destFile = new File(destinationFilePath);
try (FileOutputStream output = new FileOutputStream(destFile); PrintWriter writer = new PrintWriter(output);) {
writer.print(contents);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
return;
}
}
/**
* writes a new zip file from all of the files contained in the specified
* root directory
*
* @param rootDir
* @param zipPath
* @throws IOException
*/
public static void zip(File rootDir, String zipPath) throws IOException {
File zipFile = new File(zipPath);
if (zipFile.exists()) {
Util.delete(zipFile);
}
try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(zipFile))) {
zip(rootDir, zip, rootDir.getPath().length() + 1); // 1 for last
// slash
}
}
/**
* Writes all of the zip entries from the given directory to the specified
* zip output stream
*
* @param dir
* @param zip
* @param rootPathLength
* @throws IOException
*/
public static void zip(File dir, ZipOutputStream zip, int rootPathLength) throws IOException {
File[] files = dir.listFiles();
if (files != null) {
for (int i = 0, length = files.length; i < length; i++) {
File file = files[i];
if (file.isFile()) {
String path = file.getPath();
path = path.substring(rootPathLength);
ZipEntry entry = new ZipEntry(path.replace('\\', '/'));
zip.putNextEntry(entry);
zip.write(org.eclipse.jdt.internal.compiler.util.Util.getFileByteContent(file));
zip.closeEntry();
} else {
zip(file, zip, rootPathLength);
}
}
}
}
/**
* Returns parent's child resource matching the given resource or null if
* not found.
*
* @param resource The searched file in parent
* @return The parent's child matching the given file or null if not found.
*/
private static IResource getParentChildResource(IResource resource) {
IContainer parent = resource.getParent();
if (parent == null || !parent.exists()) {
return null;
}
try {
IResource[] members = parent.members();
int length = members == null ? 0 : members.length;
if (length > 0) {
for (int i = 0; i < length; i++) {
if (members[i] == resource) {
return members[i];
} else if (members[i].equals(resource)) {
return members[i];
} else if (members[i].getFullPath().equals(resource.getFullPath())) {
return members[i];
}
}
}
} catch (CoreException ce) {
// skip
}
return null;
}
/**
* Copy the given source (a file or a directory that must exists) to the
* given destination (a directory that must exists).
*/
public static void copyFile(String sourcePath, String destPath) {
sourcePath = Util.toNativePath(sourcePath);
destPath = Util.toNativePath(destPath);
File source = new File(sourcePath);
if (!source.exists()) {
return;
}
File dest = new File(destPath);
if (!dest.exists()) {
return;
}
if (source.isDirectory()) {
String[] files = source.list();
if (files != null) {
for (int i = 0; i < files.length; i++) {
String file = files[i];
File sourceFile = new File(source, file);
if (sourceFile.isDirectory()) {
File destSubDir = new File(dest, file);
destSubDir.mkdir();
copyFile(sourceFile.getPath(), destSubDir.getPath());
} else {
copyFile(sourceFile.getPath(), dest.getPath());
}
}
}
} else {
try (FileInputStream in = new FileInputStream(source)) {
File destFile = new File(dest, source.getName());
if (destFile.exists()) {
if (!Util.delete(destFile)) {
throw new IOException(destFile + " is in use"); //$NON-NLS-1$
}
}
try (FileOutputStream out = new FileOutputStream(destFile)) {
int bufferLength = 1024;
byte[] buffer = new byte[bufferLength];
int read = 0;
while (read != -1) {
read = in.read(buffer, 0, bufferLength);
if (read != -1) {
out.write(buffer, 0, read);
}
}
}
} catch (IOException e) {
throw new Error(e.toString());
}
}
}
/**
* Delete this resource.
*/
public static void deleteResource(IProject project) {
int retryCount = 0; // wait 1 minute at most
while (++retryCount <= 60) {
if (delete(project)) {
return;
} else {
System.gc();
}
}
throw new RuntimeException("Could not delete " + project.getFullPath()); //$NON-NLS-1$
}
public static boolean delete(IProject project) {
try {
project.delete(true, true, null);
if (org.eclipse.jdt.core.tests.util.Util.isResourceDeleted(project)) {
return true;
}
} catch (CoreException e) {
// skip
}
return org.eclipse.jdt.core.tests.util.Util.waitUntilResourceDeleted(project);
}
/**
* Copy the folder contents to the local file system.
*
* @param folder workspace folder
* @param dir local directory
*/
public static void copyFolder(IFolder folder, File dir) throws Exception {
IResource[] members = folder.members();
for (int i = 0; i < members.length; i++) {
IResource res = members[i];
if (res.getType() == IResource.FILE) {
IFile file = (IFile) res;
FileUtils.copyFile(dir, file);
} else {
IFolder nested = (IFolder) res;
File next = new File(dir, nested.getName());
next.mkdirs();
copyFolder(nested, next);
}
}
}
/**
* Copies the given file to the given directory.
*
* @param dir
* @param file
*/
public static void copyFile(File dir, IFile file) throws Exception {
File local = new File(dir, file.getName());
local.createNewFile();
try (
FileOutputStream stream = new FileOutputStream(local);
InputStream contents = file.getContents()
) {
byte[] bytes = Util.getInputStreamAsByteArray(contents, -1);
stream.write(bytes);
}
}
}