blob: 8b692f4ce81938e9cfc3b7bf50ba9b6bb82f5464 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2021 SAP AG and IBM Corporation.
* All rights reserved. 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:
* SAP AG - initial API and implementation
* IBM Corporation/Andrew Johnson - Javadoc updates
*******************************************************************************/
package org.eclipse.mat.util;
/**
* File utilities for things like copying icon files.
*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.mat.report.internal.Messages;
import org.eclipse.mat.report.internal.ReportPlugin;
public final class FileUtils
{
private static DirDeleter deleterThread;
static
{
deleterThread = new DirDeleter();
Runtime.getRuntime().addShutdownHook(deleterThread);
}
private FileUtils()
{}
/**
* Basic stream copy, the streams are already open and stay open afterward.
*
* @param in
* input stream
* @param out
* output stream
* @throws IOException
* if there was a problem with the copy
*/
public final static void copy(InputStream in, OutputStream out) throws IOException
{
byte[] b = new byte[256];
int i = 0;
while (true)
{
i = in.read(b);
if (i == -1)
break;
out.write(b, 0, i);
}
}
/**
* Create a temporary directory which should be deleted on application
* close.
*
* @param prefix
* a prefix for the new directory name
* @param parent
* a directory to put the new directory into
* @return the temporary directory, to be deleted on shutdown
* @throws IOException
* if something goes wrong
*/
public static File createTempDirectory(String prefix, File parent) throws IOException
{
File tempFile = File.createTempFile(prefix, "", parent); //$NON-NLS-1$
if (!tempFile.delete())
throw new IOException(tempFile.getAbsolutePath());
if (!tempFile.mkdir())
throw new IOException(tempFile.getAbsolutePath());
deleterThread.add(tempFile);
return tempFile;
}
public static String toFilename(String name, String extension)
{
return toFilename(name, "", extension); //$NON-NLS-1$
}
/**
* Build a file name. Convert non-letters or digits to underscore.
*
* @param prefix
* the prefix of the file
* @param suffix
* the suffix
* @param extension
* the file extension
* @return the combined file name
*/
public static String toFilename(String prefix, String suffix, String extension)
{
StringBuilder buf = new StringBuilder(prefix.length() + suffix.length() + extension.length() + 1);
for (String s : new String[] { prefix, suffix })
{
for (int ii = 0; ii < s.length() && ii < 20; ii++)
{
char c = s.charAt(ii);
if (Character.isLetterOrDigit(c))
buf.append(c);
else
buf.append("_"); //$NON-NLS-1$
}
}
buf.append(".").append(extension); //$NON-NLS-1$
return buf.toString();
}
// //////////////////////////////////////////////////////////////
// inner classes
// //////////////////////////////////////////////////////////////
private static class DirDeleter extends Thread
{
private List<File> dirList = new ArrayList<File>();
public synchronized void add(File dir)
{
if (dirList.isEmpty())
ReportPlugin.onStop(this);
dirList.add(dir);
}
public void run()
{
synchronized (this)
{
for (File dir : dirList)
deleteDirectory(dir);
dirList.clear();
}
}
private void deleteDirectory(File dir)
{
if (!dir.exists())
return;
File[] fileArray = dir.listFiles();
if (fileArray != null)
{
for (int i = 0; i < fileArray.length; i++)
{
if (fileArray[i].isDirectory())
deleteDirectory(fileArray[i]);
else if (!fileArray[i].delete())
{
ReportPlugin.log(IStatus.WARNING, MessageUtil.format(Messages.FileUtils_FileUtils_FailedToDeleteFile, fileArray[i].getAbsolutePath()));
}
}
}
if (!dir.delete())
{
ReportPlugin.log(IStatus.WARNING, MessageUtil.format(Messages.FileUtils_FailedToDeleteDirectory, dir.getAbsolutePath()));
}
}
}
/**
* Unzip the specified zip file into a directory of the same name as the zip
* file minus the suffix within the same parent directory.
*
* @param file
* .zip file
* @since 1.12
*/
public static void unzipFile(File file) throws IOException
{
String directoryName = file.getName();
directoryName = directoryName.substring(0, directoryName.lastIndexOf('.'));
File destinationDirectory = new File(file.getParentFile(), directoryName);
unzipFile(file, destinationDirectory);
}
/**
* Unzip the specified zip file into the specified destination.
*
* @param file
* .zip file
* @param destinationDirectory
* Destination directory
* @since 1.12
*/
public static void unzipFile(File file, File destinationDirectory) throws IOException
{
if (!destinationDirectory.isDirectory())
{
if (!destinationDirectory.mkdir())
{
throw new IOException(MessageUtil.format(Messages.FileUtils_FailedToMakeDirectory,
destinationDirectory.getAbsolutePath()));
}
}
try (ZipFile zipFile = new ZipFile(file))
{
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements())
{
ZipEntry entry = entries.nextElement();
File destinationFile = new File(destinationDirectory, entry.getName());
// zip slip check
if (!destinationFile.getCanonicalPath()
.startsWith(destinationDirectory.getCanonicalPath() + File.separator))
{ throw new IOException(MessageUtil.format(Messages.FileUtils_ZipSlip, entry.getName())); }
// Make any intermediate directories in the zip name if needed
File parentDirectory = destinationFile.getParentFile();
if (!parentDirectory.exists())
{
if (!parentDirectory.mkdirs())
{
throw new IOException(MessageUtil.format(Messages.FileUtils_FailedToMakeDirectory,
parentDirectory.getAbsolutePath()));
}
}
try (InputStream zipFileStream = zipFile.getInputStream(entry))
{
try (FileOutputStream unzippedFile = new FileOutputStream(destinationFile))
{
copy(zipFileStream, unzippedFile);
}
}
}
}
}
}