/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.impl.util.io;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* Created by The eXo Platform SAS.
*
* Helper contains method to perform operations with not empty directory.
*
* Date: 25 01 2011
*
* @author <a href="mailto:anatoliy.bazko@exoplatform.com.ua">Anatoliy Bazko</a>
* @version $Id: FSDirectory.java 34360 2010-11-11 11:11:11Z tolusha $
*/
public class DirectoryHelper
{
/**
* The logger instance for this class
*/
private static final Logger LOG = LoggerFactory.getLogger("exo.jcr.component.core.DirectoryHelper");
/**
* Returns the files list of whole directory including its sub directories.
*
* @param srcPath
* source path
* @return List
* @throws IOException
* if any exception occurred
*/
public static List<File> listFiles(File srcPath) throws IOException
{
List<File> result = new ArrayList<File>();
if (!srcPath.isDirectory())
{
throw new IOException(srcPath.getAbsolutePath() + " is a directory");
}
for (File subFile : srcPath.listFiles())
{
result.add(subFile);
if (subFile.isDirectory())
{
result.addAll(listFiles(subFile));
}
}
return result;
}
/**
* Copy directory.
*
* @param srcPath
* source path
* @param dstPath
* destination path
* @throws IOException
* if any exception occurred
*/
public static void copyDirectory(File srcPath, File dstPath) throws IOException
{
if (srcPath.isDirectory())
{
if (!dstPath.exists())
{
dstPath.mkdirs();
}
String files[] = srcPath.list();
for (int i = 0; i < files.length; i++)
{
copyDirectory(new File(srcPath, files[i]), new File(dstPath, files[i]));
}
}
else
{
InputStream in = null;
OutputStream out = null;
try
{
in = new FileInputStream(srcPath);
out = new FileOutputStream(dstPath);
transfer(in, out);
}
finally
{
if (in != null)
{
in.close();
}
if (out != null)
{
out.flush();
out.close();
}
}
}
}
/**
* Remove directory.
*
* @param dir
* directory to remove
* @throws IOException
* if any exception occurred
*/
public static void removeDirectory(File dir) throws IOException
{
if (dir.isDirectory())
{
for (File subFile : dir.listFiles())
{
removeDirectory(subFile);
}
if (!dir.delete())
{
throw new IOException("Can't remove folder : " + dir.getCanonicalPath());
}
}
else
{
if (!dir.delete())
{
throw new IOException("Can't remove file : " + dir.getCanonicalPath());
}
}
}
/**
* Compress data. In case when <code>rootPath</code> is a directory this method
* compress all files and folders inside the directory into single one. IOException
* will be thrown if directory is empty. If the <code>rootPath</code> is the file
* the only this file will be compressed.
*
* @param rootPath
* the root path, can be the directory or the file
* @param dstZipPath
* the path to the destination compressed file
* @throws IOException
* if any exception occurred
*/
public static void compressDirectory(File rootPath, File dstZipPath) throws IOException
{
ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(dstZipPath));
try
{
if (rootPath.isDirectory())
{
String[] files = rootPath.list();
if (files == null || files.length == 0)
{
// In java 7 no ZipException is thrown in case of an empty directory
// so to remain compatible we need to throw it explicitly
throw new ZipException("ZIP file must have at least one entry");
}
for (int i = 0; i < files.length; i++)
{
compressDirectory("", new File(rootPath, files[i]), zip);
}
}
else
{
compressDirectory("", rootPath, zip);
}
}
finally
{
if (zip != null)
{
zip.flush();
zip.close();
}
}
}
/**
* Compress files and directories.
*/
private static void compressDirectory(String relativePath, File srcPath, ZipOutputStream zip) throws IOException
{
if (srcPath.isDirectory())
{
zip.putNextEntry(new ZipEntry(relativePath + "/" + srcPath.getName() + "/"));
zip.closeEntry();
String files[] = srcPath.list();
for (int i = 0; i < files.length; i++)
{
compressDirectory(relativePath + "/" + srcPath.getName(), new File(srcPath, files[i]), zip);
}
}
else
{
InputStream in = new FileInputStream(srcPath);
try
{
zip.putNextEntry(new ZipEntry(relativePath + "/" + srcPath.getName()));
transfer(in, zip);
zip.closeEntry();
}
finally
{
if (in != null)
{
in.close();
}
}
}
}
/**
* Uncompress data to the destination directory.
*
* @param srcZipPath
* path to the compressed file, could be the file or the directory
* @param dstDirPath
* destination path
* @throws IOException
* if any exception occurred
*/
public static void uncompressDirectory(File srcZipPath, File dstDirPath) throws IOException
{
ZipInputStream in = new ZipInputStream(new FileInputStream(srcZipPath));
ZipEntry entry = null;
try
{
while ((entry = in.getNextEntry()) != null)
{
File dstFile = new File(dstDirPath, entry.getName());
dstFile.getParentFile().mkdirs();
if (entry.isDirectory())
{
dstFile.mkdirs();
}
else
{
OutputStream out = new FileOutputStream(dstFile);
try
{
transfer(in, out);
}
finally
{
out.close();
}
}
}
}
finally
{
if (in != null)
{
in.close();
}
}
}
/**
* Uncompress data to the destination directory.
*
* @param srcPath
* path to the compressed file, could be the file or the directory
* @param dstPath
* destination path
* @throws IOException
* if any exception occurred
*/
public static void uncompressEveryFileFromDirectory(File srcPath, File dstPath) throws IOException
{
if (srcPath.isDirectory())
{
if (!dstPath.exists())
{
dstPath.mkdirs();
}
String files[] = srcPath.list();
for (int i = 0; i < files.length; i++)
{
uncompressEveryFileFromDirectory(new File(srcPath, files[i]), new File(dstPath, files[i]));
}
}
else
{
ZipInputStream in = null;
OutputStream out = null;
try
{
in = new ZipInputStream(new FileInputStream(srcPath));
in.getNextEntry();
out = new FileOutputStream(dstPath);
transfer(in, out);
}
finally
{
if (in != null)
{
in.close();
}
if (out != null)
{
out.close();
}
}
}
}
/**
* Transfer bytes from in to out
*/
public static void transfer(InputStream in, OutputStream out) throws IOException
{
byte[] buf = new byte[2048];
int len;
while ((len = in.read(buf)) > 0)
{
out.write(buf, 0, len);
}
}
/**
* Rename file. Trying to remove destination first.
* If file can't be renamed in standard way the coping
* data will be used instead.
*
* @param srcFile
* source file
* @param dstFile
* destination file
* @throws IOException
* if any exception occurred
*/
public static void deleteDstAndRename(File srcFile, File dstFile) throws IOException
{
if (dstFile.exists())
{
if (!dstFile.delete())
{
throw new IOException("Cannot delete " + dstFile);
}
}
renameFile(srcFile, dstFile);
}
/**
* Rename file. If file can't be renamed in standard way the coping
* data will be used instead.
*
* @param srcFile
* source file
* @param dstFile
* destination file
* @throws IOException
* if any exception occurred
*/
public static void renameFile(File srcFile, File dstFile) throws IOException
{
// Rename the srcFile file to the new one. Unfortunately, the renameTo()
// method does not work reliably under some JVMs. Therefore, if the
// rename fails, we manually rename by copying the srcFile file to the new one
if (!srcFile.renameTo(dstFile))
{
InputStream in = null;
OutputStream out = null;
try
{
in = new FileInputStream(srcFile);
out = new FileOutputStream(dstFile);
transfer(in, out);
}
catch (IOException ioe)
{
IOException newExc = new IOException("Cannot rename " + srcFile + " to " + dstFile);
newExc.initCause(ioe);
throw newExc;
}
finally
{
if (in != null)
{
in.close();
}
if (out != null)
{
out.close();
}
}
// delete the srcFile file.
srcFile.delete();
}
}
/**
* Returns the size of directory including all subfolders.
*/
public static long getSize(File dir)
{
long size = 0;
for (File file : dir.listFiles())
{
if (file.isFile())
{
size += file.length();
}
else
{
size += getSize(file);
}
}
return size;
}
/**
* Safely close {@link Closeable} instance. Log error
* in case of exception.
*/
public static void safeClose(Closeable obj)
{
try
{
obj.close();
}
catch (IOException e)
{
LOG.error("Error while closing " + obj, e);
}
}
}