/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.common.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.modeshape.common.annotation.Immutable;
/**
* A set of utilities for working with files and directories.
*/
@Immutable
public class FileUtil {
private static FilenameFilter ACCEPT_ALL = new FilenameFilter() {
@Override
public boolean accept( File dir,
String name ) {
return true;
}
};
/**
* Delete the file or directory at the supplied path. This method works on a directory that is not empty, unlike the
* {@link File#delete()} method.
*
* @param path the path to the file or directory that is to be deleted
* @return true if the file or directory at the supplied path existed and was successfully deleted, or false otherwise
*/
public static boolean delete( String path ) {
if (path == null || path.trim().length() == 0) return false;
return delete(new File(path));
}
/**
* Delete the file or directory given by the supplied reference. This method works on a directory that is not empty, unlike
* the {@link File#delete()} method.
*
* @param fileOrDirectory the reference to the Java File object that is to be deleted
* @return true if the supplied file or directory existed and was successfully deleted, or false otherwise
*/
public static boolean delete( File fileOrDirectory ) {
if (fileOrDirectory == null) return false;
if (!fileOrDirectory.exists()) return false;
// The file/directory exists, so if a directory delete all of the contents ...
if (fileOrDirectory.isDirectory()) {
File[] files = fileOrDirectory.listFiles();
if (files != null) {
for (File childFile : files) {
delete(childFile); // recursive call (good enough for now until we need something better)
}
}
// Now an empty directory ...
}
// Whether this is a file or empty directory, just delete it ...
return fileOrDirectory.delete();
}
/**
* Copy the source file system structure into the supplied target location. If the source is a file, the destination will be
* created as a file; if the source is a directory, the destination will be created as a directory.
*
* @param sourceFileOrDirectory the file or directory whose contents are to be copied into the target location
* @param destinationFileOrDirectory the location where the copy is to be placed; does not need to exist, but if it does its
* type must match that of <code>src</code>
* @return the number of files (not directories) that were copied
* @throws IllegalArgumentException if the <code>src</code> or <code>dest</code> references are null
* @throws IOException
*/
public static int copy( File sourceFileOrDirectory,
File destinationFileOrDirectory ) throws IOException {
return copy(sourceFileOrDirectory, destinationFileOrDirectory, null);
}
/**
* Copy the source file system structure into the supplied target location. If the source is a file, the destination will be
* created as a file; if the source is a directory, the destination will be created as a directory.
*
* @param sourceFileOrDirectory the file or directory whose contents are to be copied into the target location
* @param destinationFileOrDirectory the location where the copy is to be placed; does not need to exist, but if it does its
* type must match that of <code>src</code>
* @param exclusionFilter a filter that matches files or folders that should _not_ be copied; null indicates that all files
* and folders should be copied
* @return the number of files (not directories) that were copied
* @throws IllegalArgumentException if the <code>src</code> or <code>dest</code> references are null
* @throws IOException
*/
public static int copy( File sourceFileOrDirectory,
File destinationFileOrDirectory,
FilenameFilter exclusionFilter ) throws IOException {
if (exclusionFilter == null) exclusionFilter = ACCEPT_ALL;
int numberOfFilesCopied = 0;
if (sourceFileOrDirectory.isDirectory()) {
destinationFileOrDirectory.mkdirs();
String list[] = sourceFileOrDirectory.list(exclusionFilter);
for (int i = 0; i < list.length; i++) {
String dest1 = destinationFileOrDirectory.getPath() + File.separator + list[i];
String src1 = sourceFileOrDirectory.getPath() + File.separator + list[i];
numberOfFilesCopied += copy(new File(src1), new File(dest1), exclusionFilter);
}
} else {
InputStream fin = new FileInputStream(sourceFileOrDirectory);
fin = new BufferedInputStream(fin);
try {
OutputStream fout = new FileOutputStream(destinationFileOrDirectory);
fout = new BufferedOutputStream(fout);
try {
int c;
while ((c = fin.read()) >= 0) {
fout.write(c);
}
} finally {
fout.close();
}
} finally {
fin.close();
}
numberOfFilesCopied++;
}
return numberOfFilesCopied;
}
/**
* Utility to convert {@link File} to {@link URL}.
*
* @param filePath the path of the file
* @return the {@link URL} representation of the file.
* @throws MalformedURLException
* @throws IllegalArgumentException if the file path is null, empty or blank
*/
public static URL convertFileToURL( String filePath ) throws MalformedURLException {
CheckArg.isNotEmpty(filePath, "filePath");
File file = new File(filePath.trim());
return file.toURI().toURL();
}
/**
* Determines the size (in bytes) of the file or directory at the given path.
*
* @param filePath the path of the file; may not be {@code null}
* @return the size in bytes of the file or the total computed size of the folder. If the given path is not a valid file or
* folder, this will return 0.
* @throws IOException if anything unexpected fails.
*/
public static long size(String filePath) throws IOException {
CheckArg.isNotEmpty(filePath, "filePath");
File file = new File(filePath);
if (!file.exists()) {
return 0;
}
if (file.isFile()) {
return file.length();
}
final AtomicLong size = new AtomicLong();
Files.walkFileTree(Paths.get(filePath), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) {
size.addAndGet(attrs.size());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed( Path file, IOException exc ) {
return FileVisitResult.CONTINUE;
}
});
return size.get();
}
/**
* Unzip archive to the specified destination.
*
* @param zipFile zip archive
* @param dest directory where archive will be uncompressed
* @throws IOException
*/
public static void unzip(InputStream zipFile, String dest) throws IOException {
byte[] buffer = new byte[1024];
//create output directory is not exists
File folder = new File(dest);
if (folder.exists()) {
FileUtil.delete(folder);
}
folder.mkdir();
//get the zip file content
ZipInputStream zis = new ZipInputStream(zipFile);
//get the zipped file list entry
ZipEntry ze = zis.getNextEntry();
File parent = new File(dest);
while (ze != null) {
String fileName = ze.getName();
if (ze.isDirectory()) {
File newFolder = new File(parent, fileName);
newFolder.mkdir();
} else {
File newFile = new File(parent, fileName);
try (FileOutputStream fos = new FileOutputStream(newFile)) {
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
}
}
ze = zis.getNextEntry();
}
zis.closeEntry();
zis.close();
}
/**
* Compresses directory into zip archive.
*
* @param dirName the path to the directory
* @param nameZipFile archive name.
* @throws IOException
*/
public static void zipDir(String dirName, String nameZipFile) throws IOException {
ZipOutputStream zip = null;
FileOutputStream fW = null;
fW = new FileOutputStream(nameZipFile);
zip = new ZipOutputStream(fW);
addFolderToZip("", dirName, zip);
zip.close();
fW.close();
}
/**
* Adds folder to the archive.
*
* @param path path to the folder
* @param srcFolder folder name
* @param zip zip archive
* @throws IOException
*/
public static void addFolderToZip(String path, String srcFolder, ZipOutputStream zip) throws IOException {
File folder = new File(srcFolder);
if (folder.list().length == 0) {
addFileToZip(path, srcFolder, zip, true);
} else {
for (String fileName : folder.list()) {
if (path.equals("")) {
addFileToZip(folder.getName(), srcFolder + "/" + fileName, zip, false);
} else {
addFileToZip(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip, false);
}
}
}
}
/**
* Appends file to the archive.
*
* @param path path to the file
* @param srcFile file name
* @param zip archive
* @param flag
* @throws IOException
*/
public static void addFileToZip(String path, String srcFile, ZipOutputStream zip, boolean flag) throws IOException {
File folder = new File(srcFile);
if (flag) {
zip.putNextEntry(new ZipEntry(path + "/" + folder.getName() + "/"));
} else {
if (folder.isDirectory()) {
addFolderToZip(path, srcFile, zip);
} else {
byte[] buf = new byte[1024];
int len;
FileInputStream in = new FileInputStream(srcFile);
zip.putNextEntry(new ZipEntry(path + "/" + folder.getName()));
while ((len = in.read(buf)) > 0) {
zip.write(buf, 0, len);
}
}
}
}
/**
* Returns the extension of a file, including the dot.
*
* @param filename the name of a file, may be not be null
* @return the file extension or an empty string if the extension cannot be determined.
*/
public static String getExtension(final String filename) {
Objects.requireNonNull(filename, "filename cannot be null");
int lastDotIdx = filename.lastIndexOf(".");
return lastDotIdx >= 0 ? filename.substring(lastDotIdx) : "";
}
private FileUtil() {
}
}