/*
* (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* Nuxeo - initial API and implementation
* Bogdan Stefanescu <bs@nuxeo.com>
* Estelle Giuly <egiuly@nuxeo.com>
*/
package org.nuxeo.common.utils;
import java.io.BufferedOutputStream;
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.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.zip.ZipInputStream;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public final class FileUtils {
private static final Log log = LogFactory.getLog(FileUtils.class);
// This is an utility class
private FileUtils() {
}
/**
* @deprecated since 9.1 seems unused
*/
@Deprecated
public static void safeClose(Closeable stream) {
try {
stream.close();
} catch (IOException e) {
// do nothing
}
}
/**
* @deprecated since 9.1 use {@link IOUtils#copy(InputStream, OutputStream)} instead.
*/
@Deprecated
public static void copy(InputStream in, OutputStream out) throws IOException {
IOUtils.copy(in, out);
}
/**
* @deprecated since 9.1 use {@link IOUtils#toByteArray(URL)} instead.
*/
@Deprecated
public static byte[] readBytes(URL url) throws IOException {
return IOUtils.toByteArray(url);
}
/**
* @deprecated since 9.1 use {@link IOUtils#toByteArray(InputStream)} instead.
*/
@Deprecated
public static byte[] readBytes(InputStream in) throws IOException {
return IOUtils.toByteArray(in);
}
/**
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#readLines(File)} instead.
*/
@Deprecated
public static String readFile(File file) throws IOException {
return org.apache.commons.io.FileUtils.readFileToString(file, Charsets.UTF_8);
}
/**
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#readLines(File)} instead.
*/
@Deprecated
public static List<String> readLines(File file) throws IOException {
return org.apache.commons.io.FileUtils.readLines(file);
}
/**
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeLines(File, Collection)} instead.
*/
@Deprecated
public static void writeLines(File file, List<String> lines) throws IOException {
org.apache.commons.io.FileUtils.writeLines(file, lines);
}
/**
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#readFileToByteArray(File)} instead.
*/
@Deprecated
public static byte[] readBytes(File file) throws IOException {
return org.apache.commons.io.FileUtils.readFileToByteArray(file);
}
/**
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeByteArrayToFile(File, byte[])} instead.
*/
@Deprecated
public static void writeFile(File file, byte[] buf) throws IOException {
org.apache.commons.io.FileUtils.writeByteArrayToFile(file, buf);
}
/**
* @since 5.5
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeByteArrayToFile(File, byte[], boolean)}
* instead.
*/
@Deprecated
public static void writeFile(File file, byte[] buf, boolean append) throws IOException {
org.apache.commons.io.FileUtils.writeByteArrayToFile(file, buf, append);
}
/**
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeStringToFile(File, String)} instead.
*/
@Deprecated
public static void writeFile(File file, String buf) throws IOException {
org.apache.commons.io.FileUtils.writeStringToFile(file, buf);
}
/**
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#writeStringToFile(File, String, boolean)}
* instead.
* @since 5.5
*/
@Deprecated
public static void writeFile(File file, String buf, boolean append) throws IOException {
org.apache.commons.io.FileUtils.writeStringToFile(file, buf, append);
}
/**
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#copyURLToFile(URL, File)} instead.
*/
@Deprecated
public static void download(URL url, File file) throws IOException {
org.apache.commons.io.FileUtils.copyURLToFile(url, file);
}
/**
* @deprecated since 9.1 Use {@link org.apache.commons.io.FileUtils#copyInputStreamToFile(InputStream, File)}
* instead. <strong>Note:</strong> that proposed method close the stream, could lead to issues with some
* input stream (like {@link ZipInputStream} which uses a global cursor on zip file stream when
* iterating on files in it).
*/
@Deprecated
public static void copyToFile(InputStream in, File file) throws IOException {
org.apache.commons.io.FileUtils.copyInputStreamToFile(in, file);
}
/**
* @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. See
* {@link #append(InputStream, File, boolean)} for more information.
*/
@Deprecated
public static void append(File src, File dst) throws IOException {
append(src, dst, false);
}
/**
* @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. See
* {@link #append(InputStream, File, boolean)} for more information.
*/
@Deprecated
public static void append(File src, File dst, boolean appendNewLine) throws IOException {
try (InputStream in = new FileInputStream(src)) {
append(in, dst, appendNewLine);
}
}
/**
* @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead. See
* {@link #append(InputStream, File, boolean)} for more information.
*/
@Deprecated
public static void append(InputStream in, File file) throws IOException {
append(in, file, false);
}
/**
* @deprecated since 9.1 Use {@link IOUtils#copy(InputStream, OutputStream)} instead.
*/
@Deprecated
public static void append(InputStream in, File file, boolean appendNewLine) throws IOException {
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file, true))) {
if (appendNewLine) {
out.write(System.getProperty("line.separator").getBytes());
}
IOUtils.copy(in, out);
}
}
/**
* Copies source to destination. If source and destination are the same, does nothing. Both single files and
* directories are handled.
*
* @param src the source file or directory
* @param dst the destination file or directory
* @throws IOException
*/
public static void copy(File src, File dst) throws IOException {
if (src.equals(dst)) {
return;
}
if (src.isFile()) {
copyFile(src, dst);
} else {
copyTree(src, dst);
}
}
public static void copy(File[] src, File dst) throws IOException {
for (File file : src) {
copy(file, dst);
}
}
public static void copyFile(File src, File dst) throws IOException {
if (dst.isDirectory()) {
dst = new File(dst, src.getName());
}
org.apache.commons.io.FileUtils.copyFile(src, dst, false);
}
/**
* Copies recursively source to destination.
* <p>
* The source file is assumed to be a directory.
*
* @param src the source directory
* @param dst the destination directory
* @throws IOException
*/
public static void copyTree(File src, File dst) throws IOException {
if (src.isFile()) {
copyFile(src, dst);
} else if (src.isDirectory()) {
if (dst.exists()) {
dst = new File(dst, src.getName());
dst.mkdir();
} else { // allows renaming dest dir
dst.mkdirs();
}
File[] files = src.listFiles();
for (File file : files) {
copyTree(file, dst);
}
}
}
public static void copyTree(File src, File dst, PathFilter filter) throws IOException {
copyTree(src, dst, new Path("/"), filter);
}
public static void copyTree(File src, File dst, Path prefix, PathFilter filter) throws IOException {
if (!prefix.isAbsolute()) {
prefix = prefix.makeAbsolute();
}
int rootIndex = src.getPath().length() + 1;
for (File file : src.listFiles()) {
copyTree(rootIndex, file, new File(dst, file.getName()), prefix, filter);
}
}
protected static void copyTree(int rootIndex, File src, File dst, Path prefix, PathFilter filter)
throws IOException {
if (src.isFile()) {
String relPath = src.getPath().substring(rootIndex);
if (!filter.accept(new Path(relPath))) {
return;
}
if (!prefix.isRoot()) { // remove prefix from path
String path = dst.getPath();
String pff = prefix.toString();
int prefixIndex = path.lastIndexOf(pff);
if (prefixIndex > 0) {
path = path.substring(0, prefixIndex) + path.substring(prefixIndex + pff.length());
dst = new File(path.toString());
}
}
dst.getParentFile().mkdirs();
copyFile(src, dst);
} else if (src.isDirectory()) {
File[] files = src.listFiles();
for (File file : files) {
copyTree(rootIndex, file, new File(dst, file.getName()), prefix, filter);
}
}
}
/**
* Decodes an URL path so that is can be processed as a filename later.
*
* @param url the Url to be processed.
* @return the decoded path.
*/
public static String getFilePathFromUrl(URL url) {
String path = "";
if (url.getProtocol().equals("file")) {
try {
path = URLDecoder.decode(url.getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error(e);
}
}
return path;
}
public static File getFileFromURL(URL url) {
File file;
String filename = getFilePathFromUrl(url);
if (filename.equals("")) {
file = null;
} else {
file = new File(filename);
}
return file;
}
public static String getParentPath(String path) {
int p = path.lastIndexOf(File.separator);
if (p == -1) {
return null;
}
return path.substring(0, p);
}
public static String getFileName(String path) {
int p = path.lastIndexOf(File.separator);
if (p == -1) {
return path;
}
return path.substring(p + 1);
}
public static String getFileExtension(String path) {
int p = path.lastIndexOf('.');
if (p == -1) {
return null;
}
return path.substring(p + 1);
}
public static String getFileNameNoExt(String path) {
String name = getFileName(path);
int p = name.lastIndexOf('.');
if (p == -1) {
return name;
}
return name.substring(0, p);
}
/**
* Retrieves the total path of a resource from the Thread Context.
*
* @param resource the resource name to be retrieved.
* @return the decoded path.
*/
public static String getResourcePathFromContext(String resource) {
URL url = Thread.currentThread().getContextClassLoader().getResource(resource);
return getFilePathFromUrl(url);
}
public static File getResourceFileFromContext(String resource) {
File file;
String filename = getResourcePathFromContext(resource);
if (filename.equals("")) {
file = null;
} else {
file = new File(filename);
}
return file;
}
public static File[] findFiles(File root, String pattern, boolean recurse) {
List<File> result = new ArrayList<>();
if (pattern == null) {
if (recurse) {
collectFiles(root, result);
} else {
return root.listFiles();
}
} else {
FileNamePattern pat = new FileNamePattern(pattern);
if (recurse) {
collectFiles(root, pat, result);
} else {
File[] files = root.listFiles();
for (File file : files) {
if (pat.match(file.getName())) {
result.add(file);
}
}
}
}
return result.toArray(new File[result.size()]);
}
public static void collectFiles(File root, FileNamePattern pattern, List<File> result) {
File[] files = root.listFiles();
for (File file : files) {
if (pattern.match(file.getName())) {
result.add(file);
if (file.isDirectory()) {
collectFiles(file, pattern, result);
}
}
}
}
public static void collectFiles(File root, List<File> result) {
File[] files = root.listFiles();
for (File file : files) {
result.add(file);
if (file.isDirectory()) {
collectFiles(file, result);
}
}
}
/**
* @deprecated since 9.1 Use {@link IOUtils#closeQuietly(InputStream)} instead or {@link AutoCloseable} feature.
*/
@Deprecated
public static void close(InputStream in) {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.error(e);
}
}
}
/**
* @deprecated since 9.1 Use {@link IOUtils#closeQuietly(InputStream)} instead or {@link AutoCloseable} feature.
*/
@Deprecated
public static void close(OutputStream out) {
if (out != null) {
try {
out.close();
} catch (IOException e) {
log.error(e);
}
}
}
/**
* Create a file handler (this doesn't create a real file) given a file URI. This method can be used to create files
* from invalid URL strings (e.g. containing spaces ..)
*
* @return a file object
*/
public static File urlToFile(String url) throws MalformedURLException {
return urlToFile(new URL(url));
}
public static File urlToFile(URL url) {
try {
return new File(url.toURI());
} catch (URISyntaxException e) {
return new File(url.getPath());
}
}
/**
* @deprecated since 9.1 Use {@link IOUtils#readLines(InputStream)} instead.
*/
@Deprecated
public static List<String> readLines(InputStream in) throws IOException {
return IOUtils.readLines(in);
}
/**
* Compares two files content as String even if their EOL are different
*
* @param expected a file content with Windows or Unix like EOL
* @param source another file content with Windows or Unix like EOL
* @return the result of equals after replacing their EOL
*/
public static boolean areFilesContentEquals(String expected, String source) {
if (expected == source) {
return true;
}
if (expected == null || source == null) {
return false;
}
if (expected.length() != source.length()) {
// Prevent from comparing files with Windows EOL
return expected.replace("\r\n", "\n").equals(source.replace("\r\n", "\n"));
} else {
return expected.equals(source);
}
}
/**
* Returns a safe filename, replacing unsafe characters (: \ / * ..) with "_". For instance, it turns
* "tmp/../2349:876398/foo.png" into "tmp___2349_876398_foo.png"
*
* @param filename the filename
* @return the safe filename with underscores instead of unsafe characters
* @since 9.1
*/
public static String getSafeFilename(String filename) {
return filename.replaceAll("(\\\\)|(\\/)|(\\:)|(\\*)|(\\.\\.)", "_");
}
}