/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.core.api.util;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Deque;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.weasis.core.api.Messages;
public final class FileUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUtil.class);
public static final int FILE_BUFFER = 4096;
private static final double BASE = 1024;
private static final double KB = BASE;
private static final double MB = KB * BASE;
private static final double GB = MB * BASE;
private static final DecimalFormat DEC_FORMAT = new DecimalFormat("#.##"); //$NON-NLS-1$
private static final int[] ILLEGAL_CHARS = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 34, 42, 47, 58, 60, 62, 63, 92, 124 };
private FileUtil() {
}
/**
* Transform a string into a writable string for all the operating system. All the special and control characters
* are excluded.
*
* @param fileName
* a filename or directory name
* @return a writable filename
*/
public static String getValidFileName(String fileName) {
StringBuilder cleanName = new StringBuilder();
if (fileName != null) {
for (int i = 0; i < fileName.length(); i++) {
char c = fileName.charAt(i);
if (!(Arrays.binarySearch(ILLEGAL_CHARS, c) >= 0 || (c < '\u0020') // ctrls
|| (c > '\u007e' && c < '\u00a0'))) { // ctrls
cleanName.append(c);
}
}
}
return cleanName.toString().trim();
}
public static String getValidFileNameWithoutHTML(String fileName) {
String val = null;
if (fileName != null) {
// Force to remove html tags
val = fileName.replaceAll("\\<.*?>", ""); //$NON-NLS-1$ //$NON-NLS-2$
}
return getValidFileName(val);
}
public static void safeClose(final AutoCloseable object) {
if (object != null) {
try {
object.close();
} catch (Exception e) {
LOGGER.error("Cannot close AutoCloseable", e); //$NON-NLS-1$
}
}
}
public static File createTempDir(File baseDir) {
if (baseDir != null) {
String baseName = String.valueOf(System.currentTimeMillis());
for (int counter = 0; counter < 1000; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdir()) {
return tempDir;
}
}
}
throw new IllegalStateException("Failed to create directory"); //$NON-NLS-1$
}
public static final void deleteDirectoryContents(final File dir, int deleteDirLevel, int level) {
if ((dir == null) || !dir.isDirectory()) {
return;
}
final File[] files = dir.listFiles();
if (files != null) {
for (final File f : files) {
if (f.isDirectory()) {
deleteDirectoryContents(f, deleteDirLevel, level + 1);
} else {
deleteFile(f);
}
}
}
if (level >= deleteDirLevel) {
deleteFile(dir);
}
}
public static void getAllFilesInDirectory(File directory, List<File> files) {
File[] fList = directory.listFiles();
for (File f : fList) {
if (f.isFile()) {
files.add(f);
} else if (f.isDirectory()) {
getAllFilesInDirectory(f, files);
}
}
}
private static boolean deleteFile(File fileOrDirectory) {
try {
if (!fileOrDirectory.delete()) {
LOGGER.warn("Cannot delete {}", fileOrDirectory.getPath()); //$NON-NLS-1$
return false;
}
} catch (Exception e) {
LOGGER.error("Cannot delete", e); //$NON-NLS-1$
return false;
}
return true;
}
public static boolean delete(File fileOrDirectory) {
if (fileOrDirectory == null || !fileOrDirectory.exists()) {
return false;
}
if (fileOrDirectory.isDirectory()) {
final File[] files = fileOrDirectory.listFiles();
if (files != null) {
for (File child : files) {
delete(child);
}
}
}
return deleteFile(fileOrDirectory);
}
public static void recursiveDelete(File rootDir) {
recursiveDelete(rootDir, true);
}
public static void recursiveDelete(File rootDir, boolean deleteRoot) {
if ((rootDir == null) || !rootDir.isDirectory()) {
return;
}
File[] childDirs = rootDir.listFiles();
if (childDirs != null) {
for (File f : childDirs) {
if (f.isDirectory()) {
// deleteRoot used only for the first level, directory is deleted in next line
recursiveDelete(f, false);
deleteFile(f);
} else {
deleteFile(f);
}
}
}
if (deleteRoot) {
rootDir.delete();
}
}
public static void safeClose(XMLStreamWriter writer) {
if (writer != null) {
try {
writer.close();
} catch (XMLStreamException e) {
LOGGER.error("Cannot close XMLStreamWriter", e); //$NON-NLS-1$
}
}
}
public static void safeClose(XMLStreamReader xmler) {
if (xmler != null) {
try {
xmler.close();
} catch (XMLStreamException e) {
LOGGER.error("Cannot close XMLStreamException", e); //$NON-NLS-1$
}
}
}
public static void prepareToWriteFile(File file) throws IOException {
if (!file.exists()) {
// Check the file that doesn't exist yet.
// Create a new file. The file is writable if the creation succeeds.
File outputDir = file.getParentFile();
// necessary to check exists otherwise mkdirs() is false when dir exists
if (outputDir != null && !outputDir.exists() && !outputDir.mkdirs()) {
throw new IOException("Cannot write parent directory of " + file.getPath()); //$NON-NLS-1$
}
}
}
public static String nameWithoutExtension(String fn) {
if (fn == null) {
return null;
}
int i = fn.lastIndexOf('.');
if (i > 0) {
return fn.substring(0, i);
}
return fn;
}
public static String getExtension(String fn) {
if (fn == null) {
return ""; //$NON-NLS-1$
}
int i = fn.lastIndexOf('.');
if (i > 0) {
return fn.substring(i);
}
return ""; //$NON-NLS-1$
}
public static boolean isFileExtensionMatching(File file, String[] extensions) {
if (file != null && extensions != null) {
String fileExt = getExtension(file.getName());
if (StringUtil.hasLength(fileExt)) {
return Arrays.asList(extensions).stream().anyMatch(fileExt::endsWith);
}
}
return false;
}
/**
* Write URL content into a file
*
* @param urlConnection
* @param outFile
* @throws StreamIOException
*/
public static void writeStreamWithIOException(URLConnection urlConnection, File outFile) throws StreamIOException {
try (InputStream urlInputStream = NetworkUtil.getUrlInputStream(urlConnection);
FileOutputStream outputStream = new FileOutputStream(outFile)) {
byte[] buf = new byte[FILE_BUFFER];
int offset;
while ((offset = urlInputStream.read(buf)) > 0) {
outputStream.write(buf, 0, offset);
}
outputStream.flush();
} catch (StreamIOException e) {
throw e;
} catch (IOException e) {
FileUtil.delete(outFile);
throw new StreamIOException(e);
}
}
/**
* Write inputStream content into a file
*
* @param inputStream
* @param outFile
* @throws StreamIOException
*/
public static void writeStreamWithIOException(InputStream inputStream, File outFile) throws StreamIOException {
try (FileOutputStream outputStream = new FileOutputStream(outFile)) {
byte[] buf = new byte[FILE_BUFFER];
int offset;
while ((offset = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, offset);
}
outputStream.flush();
} catch (IOException e) {
FileUtil.delete(outFile);
throw new StreamIOException(e);
} finally {
FileUtil.safeClose(inputStream);
}
}
/**
* @param inputStream
* @param out
* @return bytes transferred. O = error, -1 = all bytes has been transferred, other = bytes transferred before
* interruption
* @throws StreamIOException
*/
public static int writeStream(InputStream inputStream, File outFile) throws StreamIOException {
try (FileOutputStream outputStream = new FileOutputStream(outFile)) {
byte[] buf = new byte[FILE_BUFFER];
int offset;
while ((offset = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, offset);
}
outputStream.flush();
return -1;
} catch (SocketTimeoutException e) {
FileUtil.delete(outFile);
throw new StreamIOException(e);
} catch (InterruptedIOException e) {
FileUtil.delete(outFile);
// Specific for SeriesProgressMonitor
LOGGER.error("Interruption when writing file: {}", e.getMessage()); //$NON-NLS-1$
return e.bytesTransferred;
} catch (IOException e) {
FileUtil.delete(outFile);
throw new StreamIOException(e);
} finally {
FileUtil.safeClose(inputStream);
}
}
public static int writeFile(ImageInputStream inputStream, File outFile) throws StreamIOException {
try (FileOutputStream outputStream = new FileOutputStream(outFile)) {
byte[] buf = new byte[FILE_BUFFER];
int offset;
while ((offset = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, offset);
}
outputStream.flush();
return -1;
} catch (SocketTimeoutException e) {
FileUtil.delete(outFile);
throw new StreamIOException(e);
} catch (InterruptedIOException e) {
FileUtil.delete(outFile);
// Specific for SeriesProgressMonitor
LOGGER.error("Interruption when writing image", e.getMessage()); //$NON-NLS-1$
return e.bytesTransferred;
} catch (IOException e) {
FileUtil.delete(outFile);
throw new StreamIOException(e);
} finally {
FileUtil.safeClose(inputStream);
}
}
public static String formatSize(double size) {
StringBuilder buf = new StringBuilder();
if (size >= GB) {
buf.append(DEC_FORMAT.format(size / GB));
buf.append(' ');
buf.append(Messages.getString("FileUtil.gb")); //$NON-NLS-1$
} else if (size >= MB) {
buf.append(DEC_FORMAT.format(size / MB));
buf.append(' ');
buf.append(Messages.getString("FileUtil.mb")); //$NON-NLS-1$
} else if (size >= KB) {
buf.append(DEC_FORMAT.format(size / KB));
buf.append(' ');
buf.append(Messages.getString("FileUtil.kb")); //$NON-NLS-1$
} else {
buf.append((int) size);
buf.append(' ');
buf.append(Messages.getString("FileUtil.bytes"));//$NON-NLS-1$
}
return buf.toString();
}
public static boolean nioWriteFile(FileInputStream inputStream, FileOutputStream out) {
if (inputStream == null || out == null) {
return false;
}
try (FileChannel fci = inputStream.getChannel(); FileChannel fco = out.getChannel()) {
fco.transferFrom(fci, 0, fci.size());
return true;
} catch (Exception e) {
LOGGER.error("Write file", e); //$NON-NLS-1$
return false;
} finally {
FileUtil.safeClose(inputStream);
FileUtil.safeClose(out);
}
}
public static boolean nioWriteFile(InputStream in, OutputStream out, final int bufferSize) {
if (in == null || out == null) {
return false;
}
try (ReadableByteChannel readChannel = Channels.newChannel(in);
WritableByteChannel writeChannel = Channels.newChannel(out)) {
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
while (readChannel.read(buffer) != -1) {
buffer.flip();
writeChannel.write(buffer);
buffer.clear();
}
return true;
} catch (IOException e) {
LOGGER.error("Write file", e); //$NON-NLS-1$
return false;
} finally {
FileUtil.safeClose(in);
FileUtil.safeClose(out);
}
}
public static boolean nioCopyFile(File source, File destination) {
if (source == null || destination == null) {
return false;
}
try {
Files.copy(source.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING);
return true;
} catch (Exception e) {
LOGGER.error("Copy file", e); //$NON-NLS-1$
return false;
}
}
public static Properties readProperties(File propsFile, Properties props) {
Properties p = props == null ? new Properties() : props;
if (propsFile != null && propsFile.canRead()) {
try (FileInputStream fileStream = new FileInputStream(propsFile)) {
p.load(fileStream);
} catch (IOException e) {
LOGGER.error("Error when reading properties", e); //$NON-NLS-1$
}
}
return p;
}
public static void storeProperties(File propsFile, Properties props, String comments) {
if (props != null && propsFile != null) {
try (FileOutputStream fout = new FileOutputStream(propsFile)) {
props.store(fout, comments);
} catch (IOException e) {
LOGGER.error("Error when writing properties", e); //$NON-NLS-1$
}
}
}
public static void zip(File directory, File zipfile) throws IOException {
if (zipfile == null || directory == null) {
return;
}
URI base = directory.toURI();
Deque<File> queue = new LinkedList<>();
queue.push(directory);
// The resources will be closed in reverse order of the order in which they are created in try().
// Zip stream must be close before out stream.
try (OutputStream out = new FileOutputStream(zipfile); ZipOutputStream zout = new ZipOutputStream(out)) {
while (!queue.isEmpty()) {
File dir = queue.pop();
for (File entry : dir.listFiles()) {
String name = base.relativize(entry.toURI()).getPath();
if (entry.isDirectory()) {
queue.push(entry);
if (entry.list().length == 0) {
name = name.endsWith("/") ? name : name + "/"; //$NON-NLS-1$ //$NON-NLS-2$
zout.putNextEntry(new ZipEntry(name));
}
} else {
zout.putNextEntry(new ZipEntry(name));
copyZip(entry, zout);
zout.closeEntry();
}
}
}
}
}
public static void unzip(InputStream inputStream, File directory) throws IOException {
if (inputStream == null || directory == null) {
return;
}
try (BufferedInputStream bufInStream = new BufferedInputStream(inputStream);
ZipInputStream zis = new ZipInputStream(bufInStream)) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
File file = new File(directory, entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
} else {
file.getParentFile().mkdirs();
copyZip(zis, file);
}
}
} finally {
FileUtil.safeClose(inputStream);
}
}
public static void unzip(File zipfile, File directory) throws IOException {
if (zipfile == null || directory == null) {
return;
}
try (ZipFile zfile = new ZipFile(zipfile)) {
Enumeration<? extends ZipEntry> entries = zfile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
File file = new File(directory, entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
} else {
file.getParentFile().mkdirs();
try (InputStream in = zfile.getInputStream(entry)) {
copyZip(in, file);
}
}
}
}
}
private static void copy(InputStream in, OutputStream out) throws IOException {
if (in == null || out == null) {
return;
}
byte[] buf = new byte[FILE_BUFFER];
int offset;
while ((offset = in.read(buf)) > 0) {
out.write(buf, 0, offset);
}
out.flush();
}
private static void copyZip(File file, OutputStream out) throws IOException {
try (InputStream in = new FileInputStream(file)) {
copy(in, out);
}
}
private static void copyZip(InputStream in, File file) throws IOException {
try (OutputStream out = new FileOutputStream(file)) {
copy(in, out);
}
}
}