/*
This file is part of RouteConverter.
RouteConverter is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
RouteConverter 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with RouteConverter; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 2007 Christian Pesch. All Rights Reserved.
*/
package slash.common.io;
import slash.common.type.CompactCalendar;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static slash.common.io.InputOutput.DEFAULT_BUFFER_SIZE;
import static slash.common.type.CompactCalendar.fromMillis;
import static slash.common.type.HexadecimalNumber.encodeBytes;
/**
* Provides file and file name functionality.
*
* @author Christian Pesch
*/
public class Files {
/**
* @param file the file to find the extension
* @return the extension of the file, which are the characters
* starting with the last dot in the file name in lowercase characters
*/
public static String getExtension(File file) {
return getExtension(file.getName());
}
/**
* @param name the file name to find the extension
* @return the extension of a file name, which are the characters
* starting with the last dot in the file name in lowercase characters
*/
public static String getExtension(String name) {
int index = name.lastIndexOf(".");
if (index == -1)
return "";
return name.substring(index, name.length()).toLowerCase();
}
public static String getExtension(List<URL> urls) {
String extension = "";
for (URL url : urls) {
extension = getExtension(url.toExternalForm());
}
return extension.toLowerCase();
}
/**
* Remove the extension of the given file name, if there is any.
*
* @param name the file name to remove the extension
* @return the file name without an extension
*/
public static String removeExtension(String name) {
String extension = getExtension(name);
return name.substring(0, name.length() - extension.length());
}
/**
* Replace the extension of the given file name with the
* given extension.
*
* @param name the file name to replace the extension
* @param extension the new extension for the file name
* @return the file name with the given extension
*/
public static String setExtension(String name, String extension) {
name = removeExtension(name);
name += extension;
return name;
}
public static String extractFileName(String path) {
int index = path.lastIndexOf('/');
if (index != -1)
path = path.substring(index + 1);
return path;
}
public static File absolutize(File file) {
if (!file.isAbsolute())
file = new File(file.getAbsolutePath());
return file;
}
public static String createReadablePath(File file) {
String path = file.getAbsolutePath();
try {
path = file.getCanonicalPath();
} catch (IOException e) {
// intentionally left empty
}
return path;
}
public static String createReadablePath(URL url) {
File file = toFile(url);
if (file != null)
return createReadablePath(file);
return url.toExternalForm();
}
public static String shortenPath(String path, int maximumLength) {
if (path.length() <= maximumLength)
return path;
String lastPathFragment = lastPathFragment(path, maximumLength, true);
if (lastPathFragment.length() == maximumLength)
return lastPathFragment;
return path.substring(0, maximumLength - 3 - lastPathFragment.length()) + "..." + lastPathFragment;
}
private static String lastPathFragment(String path, int maximumLength, boolean includeSeparator) {
if (path.endsWith("/"))
path = path.substring(0, path.length() - 1);
int index = path.lastIndexOf('/');
if (index == -1)
index = path.lastIndexOf('\\');
if (index != -1)
path = path.substring(index + (includeSeparator ? 0 : 1));
if (path.length() > maximumLength - 3)
return "..." + path.substring(max(0, path.length() - maximumLength + 3));
else
return path;
}
public static String lastPathFragment(String path, int maximumLength) {
return lastPathFragment(path, maximumLength, false);
}
public static File toFile(URL url) {
if ("file".equals(url.getProtocol())) {
try {
return new File(url.toURI());
} catch (URISyntaxException e) {
// intentionally left empty
}
}
return null;
}
public static List<URL> toUrls(String... urls) {
List<URL> result = new ArrayList<>(urls.length);
for (String url : urls) {
try {
result.add(new URL(url));
} catch (MalformedURLException e) {
// fallback from URL to file
try {
result.add(new File(url).toURI().toURL());
} catch (MalformedURLException e1) {
// intentionally left empty
}
}
}
return result;
}
public static List<URL> toUrls(File... files) {
List<URL> urls = new ArrayList<>(files.length);
for (File file : files) {
try {
urls.add(file.toURI().toURL());
} catch (MalformedURLException e) {
// intentionally left empty
}
}
return urls;
}
public static List<URL> reverse(List<URL> urls) {
List<URL> result = new ArrayList<>();
for (URL url : urls)
result.add(0, url);
return result;
}
public static String createGoPalFileName(String fileName) {
fileName = fileName.toUpperCase();
fileName = fileName.replaceAll("[^\\w.]", " ");
return fileName;
}
public static String calculateConvertFileName(File file, String extension, int fileNameLength) {
String name = file.getName();
name = name.substring(0, min(name.length(), fileNameLength));
name = setExtension(name, extension);
String path = file.getParentFile() != null ? file.getParentFile().getPath() : "";
return new File(path, name).getAbsolutePath();
}
private static int calculateNumberLength(int number) {
return number > 999 ? 4 : (number > 99 ? 3 : (number > 9 ? 2 : (number > 0 ? 1 : 0)));
}
public static String numberToString(int number, int maximum) {
if (number > 9999)
throw new IllegalArgumentException("Number " + number + " is too large.");
if (maximum > 9999)
throw new IllegalArgumentException("Maximum " + maximum + " is too large.");
if (number > maximum)
throw new IllegalArgumentException("Index " + number + " larger than maximum " + maximum);
int numberLength = calculateNumberLength(maximum);
StringBuilder result = new StringBuilder(Integer.toString(number));
while (result.length() < numberLength) {
result.insert(0, "0");
}
return result.toString();
}
public static String calculateConvertFileName(File file, int index, int maximum, String extension, int fileNameLength) {
String name = file.getName();
name = removeExtension(name);
name = name.substring(0, min(name.length(), fileNameLength));
if (calculateNumberLength(maximum) > 0) {
String number = numberToString(index, maximum);
name = name.substring(0, min(name.length(), fileNameLength - number.length()));
name += number;
}
name = setExtension(name, extension);
File parentFile = file.getParentFile();
String path = parentFile != null ? parentFile.getPath() : ".";
return new File(path, name).getAbsolutePath();
}
public static File[] createTargetFiles(File pattern, int fileCount, String extension, int fileNameLength) {
File[] files = new File[fileCount];
if (fileCount == 1) {
files[0] = new File(calculateConvertFileName(pattern, extension, fileNameLength));
} else {
for (int i = 0; i < fileCount; i++) {
files[i] = new File(calculateConvertFileName(pattern, i + 1, fileCount, extension, fileNameLength));
}
}
return files;
}
public static void writePartialFile(InputStream inputStream, long fileSize, File file) throws IOException {
RandomAccessFile raf = new RandomAccessFile(file, "rw");
byte[] buffer = new byte[1024];
while (true) {
try {
int read = inputStream.read(buffer);
if (read == -1)
break;
raf.write(buffer, 0, read);
} catch (EOFException e) {
break;
}
}
raf.setLength(fileSize);
raf.close();
}
private static final String DEFAULT_ALGORITHM = "SHA1";
public static String generateChecksum(InputStream inputStream) throws IOException {
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
throw new IOException(format("Should no happen: algorithm %s not found", DEFAULT_ALGORITHM), e);
}
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int read;
while (-1 != (read = inputStream.read(buffer))) {
messageDigest.update(buffer, 0, read);
}
return encodeBytes(messageDigest.digest());
}
public static String generateChecksum(File file) throws IOException {
try (InputStream inputStream = new FileInputStream(file)) {
return generateChecksum(inputStream);
}
}
public static CompactCalendar getLastModified(File file) {
return fromMillis(file.lastModified());
}
public static void setLastModified(File file, Long lastModified) throws IOException {
if (lastModified == null)
return;
if (!file.setLastModified(lastModified))
throw new IOException(format("Could not set last modified of %s to %s", file, lastModified));
}
public static void setLastModified(File file, CompactCalendar lastModified) throws IOException {
if (lastModified == null)
return;
setLastModified(file, lastModified.getTimeInMillis());
}
/**
* Collects files/directories with the given extension in the given
* list. If path is a directory, it recursively descends the directory
* tree. If no extension is given, all files are collected.
*
* @param path the path to collect files below
* @param collectDirectories decides whether directories are collected
* @param collectFiles decides whether file are collected
* @param extension the extension in lower case
* @param list the list to add hits to
*/
private static void recursiveCollect(File path,
final boolean collectDirectories,
final boolean collectFiles,
final String extension,
final List<File> list) {
if (path.isFile()) {
if (collectFiles &&
(extension == null || getExtension(path).equals(extension)))
list.add(path);
} else {
if (collectDirectories)
list.add(path);
//noinspection ResultOfMethodCallIgnored
path.listFiles(new FileFilter() {
public boolean accept(File file) {
recursiveCollect(file, collectDirectories, collectFiles, extension, list);
return true;
}
});
}
}
/**
* Collects files below the given path with the given extension.
* If path is a directory, the collection recursively descends the
* directory tree. The extension comparison is case insensitive
*
* @param path the path to collect files below
* @param extension the case insensitively compare extension
* @return the list of files found below the given path and
* with the given extension
*/
public static List<File> collectFiles(File path, String extension) {
List<File> list = new ArrayList<>(1);
extension = extension != null ? extension.toLowerCase() : null;
recursiveCollect(path, false, true, extension, list);
return list;
}
public static List<File> collectFiles(List<File> files) {
List<File> result = new ArrayList<>();
for (File file : files) {
if (file.isFile())
result.add(file);
else if (file.isDirectory()) {
File[] list = file.listFiles(new FileFileFilter());
if (list != null)
result.addAll(asList(list));
}
}
return result;
}
public static File findExistingPath(File path) {
while (path != null && !path.exists()) {
path = path.getParentFile();
}
return path != null && path.exists() ? path : null;
}
private static void delete(File file) throws IOException {
if (file.exists() && !file.delete())
throw new IOException(format("Cannot delete %s", file));
}
public static void recursiveDelete(File path) throws IOException {
File[] files = path.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory())
recursiveDelete(file);
delete(file);
}
}
delete(path);
}
public static String printArrayToDialogString(Object[] array) {
if (array == null)
return "null";
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < array.length; i++) {
if (i > 0)
if (i == array.length - 1)
buffer.append(" and\n");
else
buffer.append(",\n");
buffer.append("'").append(shortenPath(array[i].toString(), 60)).append("'");
}
return buffer.toString();
}
}