blob: 50a8ff97047fd23f67ff342f1101168418ebcea8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.uima.pear.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.file.CopyOption;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.uima.util.impl.Constants;
/**
* The <code>FileUtil</code> class provides utility methods for working with general files.
*/
public class FileUtil {
private static final char DOT = '.';
private static final String UTF8_ENCODING = "UTF-8";
private static final String ASCII_ENCODING = "ASCII";
private static final String XML_EXTENSION = DOT + "xml";
private static final String BACKUP_EXTENSION = DOT + "bak";
private static final String ZIP_EXTENSION = DOT + "zip";
private static final char UNIX_SEPARATOR_CHAR = '/';
private static final char WINDOWS_SEPARATOR_CHAR = '\\';
private static final String UNIX_SEPARATOR = String.valueOf(UNIX_SEPARATOR_CHAR);
private static final String WINDOWS_SEPARATOR = String.valueOf(WINDOWS_SEPARATOR_CHAR);
/**
* The <code>FileTimeComparator</code> class allows comparing 'last modified' time in 2 given
* <code>File</code> objects.
*/
public static class FileTimeComparator implements Comparator<File> {
/**
* @return A negative integer, zero, or a positive integer as the first argument is less than,
* equal to, or greater than the second.
* @throws java.lang.ClassCastException
* if the arguments' types prevent them from being compared by this
* <code>Comparator</code>.
*/
@Override
public int compare(File o1, File o2) throws ClassCastException {
long t1 = o1.lastModified();
long t2 = o2.lastModified();
return (t1 >= t2) ? -1 : 1;
}
/**
* @param obj
* The reference object with which to compare.
* @return <code>true</code> only if the specified object is also a
* <code>FileTimeComparator</code>, and it imposes the same ordering as this comparator.
*/
@Override
public boolean equals(Object obj) {
return (obj instanceof FileTimeComparator);
}
} // FileTimeComparator end
/**
* The <code>DirFileFilter</code> class allows to filter files based on specified directory path
* and filename extension.
*/
public static class DirFileFilter implements FileFilter {
// attributes
private String _dirPath;
private String _fileExt;
/**
* Constructs <code>DirFileFilter</code> object for specified directory and file extension.
*
* @param dirPath
* The given directory.
* @param fileExt
* The given file extension.
*/
public DirFileFilter(String dirPath, String fileExt) {
_dirPath = (dirPath != null) ? dirPath.replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR)
: null;
if (fileExt != null) {
_fileExt = fileExt.startsWith(".") ? fileExt.toLowerCase() : "." + fileExt.toLowerCase();
} else {
_fileExt = null;
}
}
/**
* @param file
* The given file to be tested.
* @return <code>true</code> if the given file should be accepted, <code>false</code> otherwise.
*/
@Override
public boolean accept(File file) {
boolean dirAccepted = true;
boolean extAccepted = true;
if (_dirPath != null) {
String parentDir = file.getParent();
dirAccepted = parentDir != null && parentDir
.replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR).startsWith(_dirPath);
}
if (_fileExt != null) {
extAccepted = file.getPath().toLowerCase().endsWith(_fileExt);
}
return dirAccepted && extAccepted;
}
} // DirFileFilter end
/**
* The <code>NameFileFilter</code> class allows to filter files based on specified file name.
*/
public static class NameFileFilter implements FileFilter {
// attributes
private String _fileName;
/**
* Constructs <code>NameFileFilter</code> object for a given file name.
*
* @param fileName
* The given file name for filtering.
*/
public NameFileFilter(String fileName) {
_fileName = fileName.replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR);
}
/**
* @param file
* The given file to be tested.
* @return <code>true</code> if the given file should be accepted, <code>false</code> otherwise.
*/
@Override
public boolean accept(File file) {
String filePath = file.getAbsolutePath().replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR);
if (filePath.endsWith(_fileName)) {
if (filePath.length() > _fileName.length()) {
char prevChar = filePath.charAt(filePath.length() - _fileName.length() - 1);
if (prevChar == ':' || prevChar == UNIX_SEPARATOR_CHAR) {
return true;
}
} else {
return true;
}
}
return false;
}
} // NameFileFilter end
/**
* The <code>ExtFileFilter</code> allows to filter file names based on the specified filename
* extension.
*
*/
public static class ExtFilenameFilter implements FilenameFilter {
// attributes
private String _fileExt;
private boolean _ignoreCase;
/**
* Create instance of the <code>ExtFileFilter</code> class for a given filename extension. By
* default, this filename filter is case insensitive. If the given filename extension does not
* start from the '.' character, adds this character at the beginning.
*
* @param fileExt
* The given filename extension.
*/
public ExtFilenameFilter(String fileExt) {
this(fileExt, true);
}
/**
* Create instance of the <code>ExtFileFilter</code> class for a given filename extension. If a
* given <code>boolean</code> flag is <code>true</code>, this filename filter is case
* insensitive, otherwise it's case sensitive. If the given filename extension does not start
* from the '.' character, adds this character at the beginning.
*
* @param fileExt
* The given filename extension.
* @param ignoreCase
* The given 'case sensitivity' flag.
*/
public ExtFilenameFilter(String fileExt, boolean ignoreCase) {
_fileExt = fileExt.startsWith(".") ? fileExt : "." + fileExt;
_ignoreCase = ignoreCase;
if (ignoreCase) {
_fileExt = _fileExt.toLowerCase();
}
}
/**
* Tests if a specified file should be included in a file list.
*
* @param dir
* The directory in which the file was found.
* @param name
* The given name of the file.
* @return <code>true</code>, if the given file should be included in the list,
* <code>false</code> otherwise.
*/
@Override
public boolean accept(File dir, String name) {
String fileName = _ignoreCase ? name.toLowerCase() : name;
return fileName.endsWith(_fileExt);
}
} // ExtFilenameFilter end
/**
* Deletes all files and subdirectories in a given directory. In case of unsuccessful deletion,
* calls the <code>deleteOnExit()</code> method to request that files and subdirs are deleted when
* the JVM terminates.
*
* @param directory
* The given directory to be cleaned-up.
* @return The number of successfully deleted entries in the given directory.
* @throws IOException
* If an I/O exception occurred.
*/
public static int cleanUpDirectoryContent(File directory) throws IOException {
int counter = 0;
File[] allDirFiles = directory.listFiles();
if (allDirFiles != null) {
for (int i = 0; i < allDirFiles.length; i++) {
File aFile = allDirFiles[i];
if (aFile.isDirectory()) {
counter += cleanUpDirectoryContent(aFile);
if (aFile.delete()) {
counter++;
} else {
aFile.deleteOnExit();
}
} else if (aFile.isFile()) {
if (aFile.delete()) {
counter++;
} else {
aFile.deleteOnExit();
}
}
}
}
return counter;
}
/**
* Deletes all files in a given directory. In case of unsuccessful deletion, calls the
* <code>deleteOnExit()</code> method to request that files are deleted when the JVM terminates.
*
* @param directory
* The given directory to be cleaned-up.
* @return The number of successfully deleted entries in the given directory.
* @throws IOException
* If an I/O exception occurred.
*/
public static int cleanUpDirectoryFiles(File directory) throws IOException {
int counter = 0;
File[] allDirFiles = directory.listFiles();
if (allDirFiles != null) {
for (int i = 0; i < allDirFiles.length; i++) {
File aFile = allDirFiles[i];
if (aFile.isFile()) {
if (aFile.delete()) {
counter++;
} else {
aFile.deleteOnExit();
}
}
}
}
return counter;
}
/**
* Cleans-up a given directory by keeping the number of files within a given limit. Deletes the
* oldest files first. In case of unsuccessful deletion, calls the <code>deleteOnExit()</code>
* method to request that files are deleted when the JVM terminates.
*
* @param directory
* The given directory.
* @param maxLimit
* The given maximum limit of the number of files in the given directory.
* @return The number of actually deleted files.
* @throws IOException
* If an I/O exception occurred.
*/
public static int cleanUpDirectoryFiles(File directory, int maxLimit) throws IOException {
int counter = 0;
Collection<File> fileList = createFileList(directory, false);
SortedSet<File> sortedFileSet = sortFileListByTime(fileList);
if (sortedFileSet.size() > maxLimit) {
Iterator<File> list = sortedFileSet.iterator();
int no = 0;
while (list.hasNext()) {
File file = list.next();
no++;
if (no > maxLimit) {
if (file.delete()) {
counter++;
} else {
file.deleteOnExit();
}
}
}
}
return counter;
}
private static String normalizeToUnix(String aPath) {
if (aPath == null) {
return null;
}
return aPath.replace(WINDOWS_SEPARATOR_CHAR, UNIX_SEPARATOR_CHAR);
}
/**
* Computes relative path to a given file from a given reference directory, if both the reference
* directory and the file are in the same logical file system (partition).
*
* @param referenceDir
* The given reference directory.
* @param file
* The given file.
* @return The relative path to the given file from the given reference directory, or
* <code>null</code>, if the relative path does not exist.
* @throws IOException
* If an I/O error occurs, which is possible because the construction of the canonical
* pathname may require filesystem queries.
*/
public static String computeRelativePath(File referenceDir, File file) throws IOException {
// get canonical path expressions
String refPath = normalizeToUnix(referenceDir.getCanonicalPath());
String filePath = normalizeToUnix(file.getCanonicalPath());
// compute relative path from reference dir to file dir-tree
StringBuffer relBuffer = new StringBuffer();
while (refPath != null && !filePath.startsWith(refPath)) {
relBuffer.append("../");
refPath = normalizeToUnix((new File(refPath)).getParent());
}
if (refPath != null) {
// construct relative path
String subPath = filePath.substring(refPath.length());
if (relBuffer.length() == 0) {
relBuffer.append("./");
}
if (subPath.startsWith("/")) {
relBuffer.append(subPath.substring(1));
} else {
relBuffer.append(subPath);
}
return relBuffer.toString();
}
// relative path does not exist
return null;
}
/**
* Copies the content of a given source file to a given destination file.
*
* @param source
* The given source file.
* @param destination
* The given destination file.
* @return <code>true</code> if the copy operation completed successfully, <code>false</code>
* otherwise.
* @throws IOException
* If any I/O exception occurred.
* @deprecated use Java 7 for this see {@link java.nio.file.Files#copy(Path, Path, CopyOption...)}
*/
@Deprecated
public static boolean copyFile(File source, File destination) throws IOException {
try (BufferedInputStream iStream = new BufferedInputStream(new FileInputStream(source));
BufferedOutputStream oStream = new BufferedOutputStream(
new FileOutputStream(destination))) {
byte[] block = new byte[4096];
int bCount = 0;
while ((bCount = iStream.read(block)) > 0) {
oStream.write(block, 0, bCount);
}
}
return true;
}
/**
* Copies the content of a given remote source file to a given destination file.
*
* @param sourceUrl
* The given source file URL.
* @param destination
* The given destination file.
* @return <code>true</code> if the copy operation completed successfully, <code>false</code>
* otherwise.
* @throws IOException
* If any I/O exception occurred.
* @deprecated use Java 7 for this see
* {@link java.nio.file.Files#copy(InputStream, Path, CopyOption...)}
*/
@Deprecated
public static boolean copyFile(URL sourceUrl, File destination) throws IOException {
try (BufferedInputStream iStream = new BufferedInputStream(sourceUrl.openStream());
BufferedOutputStream oStream = new BufferedOutputStream(
new FileOutputStream(destination))) {
byte[] block = new byte[4096];
int bCount = 0;
while ((bCount = iStream.read(block)) > 0) {
oStream.write(block, 0, bCount);
}
}
return true;
}
/**
* Creates list of subdirectories in a given root directory, including all its subdirectories.
*
* @param rootDir
* The given root directory.
* @return <code>Collection</code> of <code>File</code> objects, representing subdirectories in
* the given root directory and all its subdirectories.
*
* @throws java.io.IOException
* If any I/O exception occurs.
*/
public static Collection<File> createDirList(File rootDir) throws IOException {
return createDirList(rootDir, true);
}
/**
* Creates list of subdirectories in a given root directory. If a given <code>boolean</code> flag
* is <code>true</code>, all the subdirectories of the given root directory are also scanned,
* otherwise only subdirectories in the given root directory are included.
*
* @return <code>Collection</code> of <code>File</code> objects, representing subdirectories in
* the given root directory.
* @param rootDir
* The given root directory.
* @param includeSubdirs
* If <code>true</code>, the returned list includes sub-directories from all
* sub-directories of the given root directory, otherwise it includes only
* sub-directories from the given root directory itself.
*
* @exception java.io.IOException
* If any I/O exception occurs.
*/
public static Collection<File> createDirList(File rootDir, boolean includeSubdirs)
throws IOException {
ArrayList<File> listOfDirs = new ArrayList<>();
File[] allDirFiles = rootDir.listFiles();
if (allDirFiles == null) {
throw new FileNotFoundException("invalid directory specified");
}
for (int i = 0; i < allDirFiles.length; i++) {
File aFile = allDirFiles[i];
if (aFile.isDirectory()) {
listOfDirs.add(aFile);
if (includeSubdirs) {
listOfDirs.addAll(createDirList(aFile, includeSubdirs));
}
}
}
return listOfDirs;
}
/**
* Creates a list of directories in a given archive (JAR) file. The root directory path, used to
* represent the directories, is set to the input archive file path without the file name
* extension.
*
* @param archive
* The input archive (JAR) file.
* @return <code>Collection</code> of <code>File</code> objects, representing directories in the
* given archive file.
* @throws IOException
* If any I/O exception occurs.
*/
public static Collection<File> createDirList(JarFile archive) throws IOException {
ArrayList<File> listOfDirs = new ArrayList<>();
// set root_dir_path = archive_file_path (w/o file name extension)
int nameEndIndex = archive.getName().lastIndexOf(DOT);
String rootDirPath = (nameEndIndex > 0) ? archive.getName().substring(0, nameEndIndex)
: archive.getName();
File rootDir = new File(rootDirPath);
// add directories to the list
Enumeration<JarEntry> entries = archive.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
File file = new File(rootDir, entry.getName());
if (entry.isDirectory()) {
listOfDirs.add(file);
} else {
// make sure the parent dir is added
File parentDir = file.getParentFile();
while (!parentDir.equals(rootDir)) {
if (!listOfDirs.contains(parentDir)) {
listOfDirs.add(parentDir);
}
parentDir = parentDir.getParentFile();
}
}
}
return listOfDirs;
}
/**
* Creates list of files in a given directory, including all its subdirectories.
*
* @return <code>Collection</code> of <code>File</code> objects in the given directory, including
* all its subdirectories.
* @param filesDir
* The given directory.
*
* @exception java.io.IOException
* If any I/O exception occurs.
*/
public static Collection<File> createFileList(File filesDir) throws IOException {
return createFileList(filesDir, true);
}
/**
* Creates list of files in a given directory. If a given <code>boolean</code> flag is
* <code>true</code>, all the sub-directories of the given directory are also scanned, otherwise
* only files in the given directory are included.
*
* @return <code>Collection</code> of <code>File</code> objects in the given directory.
* @param filesDir
* The given directory.
* @param includeSubdirs
* If <code>true</code>, the returned file list includes files from all the
* sub-directories of the given directory, otherwise it includes only files from the
* given directory itself.
*
* @exception java.io.IOException
* If any I/O exception occurs.
*/
public static Collection<File> createFileList(File filesDir, boolean includeSubdirs)
throws IOException {
ArrayList<File> listOfFiles = new ArrayList<>();
File[] allDirFiles = filesDir.listFiles();
if (allDirFiles == null) {
throw new FileNotFoundException("invalid directory specified");
}
for (int i = 0; i < allDirFiles.length; i++) {
File aFile = allDirFiles[i];
if (aFile.isDirectory() && includeSubdirs) {
listOfFiles.addAll(createFileList(aFile, includeSubdirs));
} else if (!aFile.isDirectory()) {
listOfFiles.add(aFile);
}
}
return listOfFiles;
}
/**
* Creates a list of files in a given archive (JAR) file. The root directory path, used to
* represent the files, is set to the input archive file path without the file name extension.
*
* @param archive
* The input archive (JAR) file.
* @return <code>Collection</code> of <code>File</code> objects, representing files in the given
* archive file.
* @throws IOException
* If any I/O exception occurs.
*/
public static Collection<File> createFileList(JarFile archive) throws IOException {
ArrayList<File> listOfFiles = new ArrayList<>();
// set root_dir_path = archive_file_path (w/o file name extension)
int nameEndIndex = archive.getName().lastIndexOf(DOT);
String rootDirPath = (nameEndIndex > 0) ? archive.getName().substring(0, nameEndIndex)
: archive.getName();
File rootDir = new File(rootDirPath);
// add directories to the list
Enumeration<JarEntry> entries = archive.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
File file = new File(rootDir, entry.getName());
if (!entry.isDirectory()) {
listOfFiles.add(file);
}
}
return listOfFiles;
}
/**
* Creates a new empty file in a directory specified by the 'java.io.tmpdir' or the 'user.home'
* system property, using given prefix and suffix strings to generate its name. For more
* information see the documentation on the <code>java.io.File.createTempFile()</code> method.
*
* @param prefix
* The given prefix string to be used in generating the file's name; must be at least
* three characters long.
* @param suffix
* The given suffix string to be used in generating the file's name; may be
* <code>null</code>, in which case the suffix ".tmp" will be used.
* @return The <code>File</code> object denoting the newly created file.
* @throws IOException
* If a temporary directory not found or other I/O exception occurred.
* @deprecated use Java 7 method for this see
* {@link java.io.File#createTempFile(String, String, File)}
*/
@Deprecated
public static File createTempFile(String prefix, String suffix) throws IOException {
String tempDirPath = System.getProperty("java.io.tmpdir");
if (tempDirPath == null) {
tempDirPath = System.getProperty("user.home");
}
if (tempDirPath == null) {
throw new IOException("could not find temporary directory");
}
File tempDir = new File(tempDirPath);
if (!tempDir.isDirectory()) {
throw new IOException("temporary directory not available");
}
return File.createTempFile(prefix, suffix, tempDir);
}
/**
* Deletes a given directory, including all its subdirectories and files. Returns
* <code>true</code> if the deletion was successful, otherwise returns <code>false</code>. In case
* of unsuccessful deletion, calls <code>deleteOnExit()</code> method to request that files and
* subdirs be deleted when the virtual machine terminates.
*
* @param dir
* The given directory to be deleted.
* @return <code>true</code> if the deletion was successful, otherwise <code>false</code>.
* @throws java.io.IOException
* If any I/O exception occurs.
*/
public static boolean deleteDirectory(File dir) throws IOException {
boolean done = true;
// list immediate files/subdirs in this dir
File[] fileList = dir.listFiles();
// first, delete plain files and sub-directories (recursive)
for (int i = 0; i < fileList.length; i++) {
File entry = fileList[i];
if (entry.isDirectory()) {
done = deleteDirectory(entry);
} else if (!entry.delete()) {
entry.deleteOnExit();
done = false;
}
}
// second, delete the root dir itself
if (!dir.delete()) {
dir.deleteOnExit();
done = false;
}
return done;
}
/**
* Extracts all files in a given JAR directory (including all its subdirectories) from a given JAR
* file to a given target directory.
*
* @param jarFile
* The given JAR file.
* @param dirPath
* The given JAR directory.
* @param targetDir
* The given target directory.
* @return Total number of bytes extracted.
* @throws IOException
* If any I/O exception occurred.
*/
public static long extractDirectoryFromJar(JarFile jarFile, String dirPath, File targetDir)
throws IOException {
return extractFilesFromJar(jarFile, targetDir, new DirFileFilter(dirPath, null));
}
/**
* Extracts all files that have a given extension from a given JAR file to a given target
* directory. To extract files without extension, use <code>null</code> as the
* <code>fileExt</code> parameter.
*
* @param jarFile
* The given JAR file.
* @param fileExt
* The given file extension.
* @param targetDir
* The given target directory.
* @return Total number of bytes extracted.
* @throws IOException
* If any I/O exception occurred.
*/
public static long extractFilesWithExtFromJar(JarFile jarFile, String fileExt, File targetDir)
throws IOException {
return extractFilesFromJar(jarFile, targetDir, new DirFileFilter(null, fileExt));
}
/**
* Extracts all files from a given JAR file to a given target directory.
*
* @param jarFile
* The given JAR file.
* @param targetDir
* The given target directory.
* @return Total number of bytes extracted.
* @throws IOException
* If any I/O exception occurred.
*/
public static long extractFilesFromJar(JarFile jarFile, File targetDir) throws IOException {
return extractFilesFromJar(jarFile, targetDir, null);
}
/**
* Extracts files from a given JAR file to a given target directory, based on a given
* <code>FileFilter</code> object.
*
* @param jarFile
* The given JAR file.
* @param targetDir
* The given target directory.
* @param filter
* The given <code>FileFilter</code> object.
* @return Total number of bytes extracted.
* @throws IOException
* If any I/O exception occurred.
*/
public static long extractFilesFromJar(JarFile jarFile, File targetDir, FileFilter filter)
throws IOException {
long totalBytes = 0;
byte[] block = new byte[4096];
String prefix = normalizeToUnix(targetDir.getCanonicalPath());
if (!prefix.endsWith(UNIX_SEPARATOR)) {
prefix = prefix + UNIX_SEPARATOR_CHAR;
}
Enumeration<JarEntry> jarList = jarFile.entries();
while (jarList.hasMoreElements()) {
JarEntry jarEntry = jarList.nextElement();
if (jarEntry.isDirectory()) {
continue;
}
// check that file is accepted
if (filter != null && !filter.accept(new File(jarEntry.getName()))) {
continue;
}
// make sure the file directory exists
File file = new File(targetDir, jarEntry.getName());
if (!normalizeToUnix(file.getCanonicalPath()).startsWith(prefix)) {
throw new IOException("Can only write within target folder [" + targetDir.getAbsolutePath()
+ "]. Please validate ZIP contents.");
}
File dir = file.getParentFile();
if (!dir.exists() && !dir.mkdirs()) {
throw new IOException("Could not create directory [" + dir.getAbsolutePath() + "]");
}
// extract file
try (BufferedInputStream iStream = new BufferedInputStream(jarFile.getInputStream(jarEntry));
BufferedOutputStream oStream = new BufferedOutputStream(new FileOutputStream(file))) {
int bCount = 0;
while ((bCount = iStream.read(block)) > 0) {
totalBytes += bCount;
oStream.write(block, 0, bCount);
}
}
}
return totalBytes;
}
/**
* Constructs an absolute path of a given object, located in a given root directory, based on its
* relative path in this directory.
*
* @param rootDir
* The given root directory.
* @param relativePath
* The given relative path of the object.
* @return The absolute path for the given object, located in the given root directory.
*/
public static String getAbsolutePath(File rootDir, String relativePath) {
File object = new File(rootDir, relativePath);
return object.getAbsolutePath();
}
/**
* Identifies a given file name extension.
*
* @param fileName
* The given file name.
* @return The file name extension
*/
public static String getFileNameExtension(String fileName) {
StringBuffer buffer = new StringBuffer();
int begIndex = fileName.lastIndexOf(DOT);
if (begIndex > 0) {
buffer.append(DOT);
for (int i = begIndex + 1; i < fileName.length(); i++) {
char ch = fileName.charAt(i);
if (Character.isLetterOrDigit(ch)) {
buffer.append(ch);
} else {
break;
}
}
}
return buffer.toString();
}
/**
* Returns file size for a given file.
*
* @param fileLocation
* The given file location - local file path or URL.
* @return The given file size, if the specified file can be accessed, -1 otherwise.
* @deprecated use Java 7 method for this see {@link java.nio.file.Files#size(Path)}
*/
@Deprecated
public static long getFileSize(String fileLocation) {
long fileSize = 0;
// choose file size method: local FS or HTTP
File file = new File(fileLocation);
if (file.isFile()) {
fileSize = file.length();
} else {
try {
URL fileUrl = new URL(fileLocation);
URLConnection urlConn = fileUrl.openConnection();
// See https://issues.apache.org/jira/browse/UIMA-1746
urlConn.setUseCaches(false);
fileSize = urlConn.getContentLength();
} catch (IOException e) {
fileSize = -1;
}
}
return fileSize;
}
/**
* Constructs a relative path of a given object, located in a given root directory, based on its
* absolute path.
*
* @param rootDir
* The given root directory.
* @param absolutePath
* The given absolute path of the object.
* @return The relative path of the given object, located in the given root directory.
*/
public static String getRelativePath(File rootDir, String absolutePath) {
String rootDirPath = normalizeToUnix(rootDir.getAbsolutePath());
String objectPath = normalizeToUnix(absolutePath);
if (objectPath.startsWith(rootDirPath)) {
objectPath = objectPath.substring(rootDirPath.length());
}
if (objectPath.startsWith("/")) {
objectPath = objectPath.substring(1);
}
return objectPath;
}
/**
* Makes and attempt to identify possible UTF signature (BOM) in a given sequence of bytes.
* Returns the identified UTF signature name or <code>null</code>, if the signature could not be
* identified. For more on UTF and its signatures see
* <a href="http://www.unicode.org/faq/utf_bom.html" target="_blank"> FAQ - UTF and BOM</a>.
*
* @param prefix
* The given sequence of bytes to analyze.
* @param length
* The length of the given sequence of bytes.
* @return The UTF signature name or <code>null</code>, if the signature could not be identified.
*/
public static String identifyUtfSignature(int[] prefix, int length) {
String utfSignature = null;
if (length == 3) {
// check for UTF-8 signature
if (prefix[0] == 0xEF && prefix[1] == 0xBB && prefix[2] == 0xBF) {
utfSignature = UTF8_ENCODING;
}
} else if (length == 2) {
// check for UTF-16 signature
if (prefix[0] == 0xFE && prefix[1] == 0xFF) {
utfSignature = "UTF-16BE";
} else if (prefix[0] == 0xFF && prefix[1] == 0xFE) {
utfSignature = "UTF-16LE";
}
} else if (length == 4) {
// check for UTF-32 signature
if (prefix[0] == 0x00 && prefix[1] == 0x00 && prefix[2] == 0xFE && prefix[3] == 0xFF) {
utfSignature = "UTF-32BE";
} else if (prefix[0] == 0xFF && prefix[1] == 0xFE && prefix[2] == 0x00 && prefix[3] == 0x00) {
utfSignature = "UTF-32LE";
}
}
return utfSignature;
}
/**
* Returns <code>true</code>, if a given text file contains only ASCII characters, otherwise
* returns <code>false</code>.
*
* @param textFile
* The given text file.
* @return <code>true</code>, if the given text file contains only ASCII characters,
* <code>false</code> otherwise.
* @throws IOException
* If an I/O exception occurred.
*/
public static boolean isAsciiFile(File textFile) throws IOException {
try (FileInputStream iStream = new FileInputStream(textFile)) {
return isAsciiStream(iStream);
}
}
/**
* Returns <code>true</code>, if a given input stream contains only ASCII characters, otherwise
* returns <code>false</code>.
*
* @param iStream
* The given input stream.
* @return <code>true</code>, if the given input stream contains only ASCII characters,
* <code>false</code> otherwise.
* @throws IOException
* If an I/O exception occurred.
*/
public static boolean isAsciiStream(InputStream iStream) throws IOException {
boolean isAscii = true;
int nextByte = 0;
while ((nextByte = iStream.read()) >= 0) {
if (nextByte > 127) {
isAscii = false;
break;
}
}
return isAscii;
}
/**
* Loads a list of non-empty EOL-delimited strings from a given text stream.
*
* @param iStream
* The given input text stream.
* @return The array of non-empty strings loaded from the given text stream.
* @throws IOException
* If any I/O exception occurred.
*/
public static String[] loadListOfStrings(BufferedReader iStream) throws IOException {
String[] outputArray = null;
List<String> outputList = new ArrayList<>();
String line = null;
while ((line = iStream.readLine()) != null) {
String string = line.trim();
if (string.length() > 0) {
outputList.add(string);
}
}
if (outputList.size() > 0) {
outputArray = new String[outputList.size()];
outputList.toArray(outputArray);
}
return (outputArray != null) ? outputArray : Constants.EMPTY_STRING_ARRAY;
}
/**
* Loads a list of non-empty EOL-delimited strings from a given text file using the default file
* encoding.
*
* @param textFile
* The given text file.
* @return The array of non-empty strings loaded from the given text file.
* @throws IOException
* If any I/O exception occurred.
* @deprecated use Java 7 method for this see
* {@link java.nio.file.Files#readAllLines(Path, Charset)}
*/
@Deprecated
public static String[] loadListOfStrings(File textFile) throws IOException {
String[] outputArray;
try (BufferedReader iStream = new BufferedReader(
new InputStreamReader(new FileInputStream(textFile)))) {
outputArray = loadListOfStrings(iStream);
}
return (outputArray != null) ? outputArray : Constants.EMPTY_STRING_ARRAY;
}
/**
* Loads a list of non-empty EOL-delimited strings from a given remote text file.
*
* @param textFileURL
* The URL of the given input text file.
* @return The array of non-empty strings loaded from the given text file.
* @throws IOException
* If any I/O exception occurred.
*/
public static String[] loadListOfStrings(URL textFileURL) throws IOException {
URLConnection urlConnection = textFileURL.openConnection();
// See https://issues.apache.org/jira/browse/UIMA-1746
urlConnection.setUseCaches(false);
String[] outputArray;
try (BufferedReader iStream = new BufferedReader(
new InputStreamReader(urlConnection.getInputStream()))) {
outputArray = loadListOfStrings(iStream);
}
return (outputArray != null) ? outputArray : Constants.EMPTY_STRING_ARRAY;
}
/**
* Loads a specified properties file from a given JAR file.
*
* @param propFilePath
* The given properties file path in the JAR file.
* @param jarFile
* The given JAR file.
* @return <code>Properties</code> object containing loaded properties, or <code>null</code>, if
* the properties file was not found in the given JAR file.
* @throws IOException
* If any I/O exception occurred.
*/
public static Properties loadPropertiesFromJar(String propFilePath, JarFile jarFile)
throws IOException {
Properties properties = null;
String name = normalizeToUnix(propFilePath);
JarEntry jarEntry = jarFile.getJarEntry(name);
if (jarEntry != null) {
try (InputStream iStream = jarFile.getInputStream(jarEntry)) {
properties = new Properties();
properties.load(iStream);
}
}
return properties;
}
/**
* Loads a text file associated with a given input stream.
*
* @param iStream
* The given text input stream.
* @return The content of the text file.
* @throws IOException
* If any I/O exception occurs.
*/
public static String loadTextFile(BufferedReader iStream) throws IOException {
try (StringWriter buffer = new StringWriter(); PrintWriter writer = new PrintWriter(buffer);) {
String line = null;
while ((line = iStream.readLine()) != null) {
writer.println(line);
}
return buffer.toString();
}
}
/**
* Loads a given local text file using the default file encoding.
*
* @return The content of the text file.
* @param textFile
* The given text file.
* @throws IOException
* If any I/O exception occurs.
* @deprecated use main file util for this, see
* {@link org.apache.uima.util.FileUtils#file2String(File)} if using the default
* charset is OK
*/
@Deprecated
public static String loadTextFile(File textFile) throws IOException {
try (BufferedReader iStream = new BufferedReader(new FileReader(textFile))) {
return loadTextFile(iStream);
}
}
/**
* Loads a given local text file using a specified file encoding.
*
* @return The content of the text file.
* @param textFile
* The given text file.
* @param encoding
* The given text file encoding name.
* @throws IOException
* If any I/O exception occurs.
* @deprecated use main file util for this, see
* {@link org.apache.uima.util.FileUtils#file2String(File, String)} if using the
* default Charset is OK
*/
@Deprecated
public static String loadTextFile(File textFile, String encoding) throws IOException {
try (BufferedReader iStream = new BufferedReader(
new InputStreamReader(new FileInputStream(textFile), encoding))) {
return loadTextFile(iStream);
}
}
/**
* Loads a given remote text file.
*
* @param textFileURL
* The given text file URL.
* @return The content of the text file.
* @throws IOException
* If any I/O exception occurs.
*/
public static String loadTextFile(URL textFileURL) throws IOException {
URLConnection urlConnection = textFileURL.openConnection();
// See https://issues.apache.org/jira/browse/UIMA-1746
urlConnection.setUseCaches(false);
return loadTextFile(urlConnection);
}
/**
* Loads a given remote text file.
*
* @param urlConnection
* The given URL connection.
* @return The content of the text file.
* @throws IOException
* If any I/O exception occurs.
*/
public static String loadTextFile(URLConnection urlConnection) throws IOException {
// See https://issues.apache.org/jira/browse/UIMA-1746
urlConnection.setUseCaches(false);
try (BufferedReader iStream = new BufferedReader(
new InputStreamReader(urlConnection.getInputStream()))) {
return loadTextFile(iStream);
}
}
/**
* Loads a specified text file from a given JAR file.
*
* @param filePath
* The specified text file path inside the JAR file.
* @param jarFile
* The given JAR file.
* @return The content of the text specified file, or <code>null</code>, if the text file was not
* found in the given JAR file.
* @throws IOException
* If any I/O exception occurs.
*/
public static String loadTextFileFromJar(String filePath, JarFile jarFile) throws IOException {
String content = null;
String name = normalizeToUnix(filePath);
JarEntry jarEntry = jarFile.getJarEntry(name);
if (jarEntry != null) {
try (BufferedReader iStream = new BufferedReader(
new InputStreamReader(jarFile.getInputStream(jarEntry)))) {
content = loadTextFile(iStream);
}
}
return content;
}
/**
* Converts a given input file path into a valid file URL string.
*
* @param path
* The given file path to be converted.
* @return The file URL string for the specified file.
*/
public static String localPathToFileUrl(String path) {
// get absolute path
File file = new File(path);
String absPath = normalizeToUnix(file.getAbsolutePath());
// construct file URL
StringBuffer urlBuffer = new StringBuffer("file:///");
urlBuffer.append(absPath.replace(':', '|'));
String fileUrlString = urlBuffer.toString().replaceAll(" ", "%20");
URL fileUrl = null;
try {
fileUrl = new URL(fileUrlString);
} catch (MalformedURLException e) {
fileUrl = null;
}
return (fileUrl != null) ? fileUrl.toExternalForm() : fileUrlString;
}
/**
* Moves a given source file to a given destination directory.
*
* @param source
* The given source file.
* @param destinationDir
* The given destination directory.
* @return <code>true</code> if the move operation completed successfully, <code>false</code>
* otherwise.
* @throws IOException
* If any I/O exception occurred.
* @deprecated use Java 7 for this see {@link java.nio.file.Files#move(Path, Path, CopyOption...)}
*/
@Deprecated
public static boolean moveFile(File source, File destinationDir) throws IOException {
boolean completed = false;
File destination = new File(destinationDir, source.getName());
if (destination.exists()) {
destination.delete();
}
if (copyFile(source, destination)) {
completed = source.delete();
}
return completed;
}
/**
* Replaces all occurrences of a given regular expression with a given string in a given text
* file. Supports only 1 file encoding - ASCII - for all general text files. Supports 2 encodings
* - UTF-8 (ASCII) and UTF-16 for XML files.
*
* @param textFile
* The given text file.
* @param subStringRegex
* The given regular expression string to be replaced.
* @param replacement
* The given replacement string.
* @return The number of actual string replacements performed.
* @throws IOException
* If any I/O exception occurs.
*/
public static int replaceStringInFile(File textFile, String subStringRegex, String replacement)
throws IOException {
int counter = 0;
// for general text file - supporting ASCII encoding only
String encoding = ASCII_ENCODING;
// check file extension
int extIndex = textFile.getName().lastIndexOf(DOT);
String fileExt = (extIndex > 0) ? textFile.getName().substring(extIndex) : null;
if (XML_EXTENSION.equalsIgnoreCase(fileExt)) {
// for XML file - supporting UTF-8 (ASCII) and UTF-16 encodings
String xmlEncoding = XMLUtil.detectXmlFileEncoding(textFile);
if (xmlEncoding != null) {
encoding = xmlEncoding;
} else {
encoding = UTF8_ENCODING;
}
}
// load text file, using supported encoding
String fileContent = loadTextFile(textFile, encoding);
boolean done = false;
File backupFile = null;
// get pattern for given regex
Pattern pattern = Pattern.compile(subStringRegex);
// format replacement string
String replaceWith = StringUtil.toRegExpReplacement(replacement);
try {
// save backup copy of input file
backupFile = new File(textFile.getAbsolutePath() + BACKUP_EXTENSION);
if (backupFile.exists()) {
backupFile.delete();
}
if (!textFile.renameTo(backupFile)) {
throw new IOException("can't save backup copy of " + textFile.getAbsolutePath());
}
try (BufferedReader sReader = new BufferedReader(new StringReader(fileContent));
PrintStream fStream = new PrintStream(new FileOutputStream(textFile), true,
encoding);) {
String srcLine = null;
while ((srcLine = sReader.readLine()) != null) {
// count pattern matches in the source string
Matcher matcher = pattern.matcher(srcLine);
while (matcher.find()) {
counter++;
}
// replace all pattern matches in the source string
String resLine = srcLine.replaceAll(subStringRegex, replaceWith);
fStream.println(resLine);
}
}
done = true;
} catch (IOException exc) {
throw exc;
} catch (Throwable err) {
if (err instanceof IOException) {
throw new IOException(err.toString() + " in " + textFile.getAbsolutePath());
}
throw new RuntimeException(err.toString() + " in " + textFile.getAbsolutePath());
} finally {
if (done) {
// remove backup file
if (backupFile != null) {
backupFile.delete();
}
} else {
// restore input file
textFile.delete();
if (backupFile != null) {
backupFile.renameTo(textFile);
}
}
}
return counter;
}
/**
* Sorts a given list of files by the 'last modified' time in the descending order.
*
* @param fileList
* The given list of files.
* @return The list of files sorted by the 'last modified' time in the descending order.
*/
public static SortedSet<File> sortFileListByTime(Collection<File> fileList) {
TreeSet<File> set = new TreeSet<>(new FileTimeComparator());
set.addAll(fileList);
return set;
}
/**
* Zips the contents of a given directory. The output ZIP file, by default, is created in the
* given directory, and its name is the given directory name with 'zip' extension.
*
* @param dir2zip
* The given directory to be zipped.
* @return The output ZIP file.
* @throws IOException
* If any I/O exception occurred.
*/
public static File zipDirectory(File dir2zip) throws IOException {
// construct zipped file path
String zipFileName = dir2zip.getName() + ZIP_EXTENSION;
File zipFile = new File(dir2zip, zipFileName);
return zipDirectory(dir2zip, zipFile);
}
/**
* Zips the contents of a given directory to a given output ZIP file.
*
* @param dir2zip
* The given directory to be zipped.
* @param zippedFile
* The given output ZIP file.
* @return The output ZIP file.
* @throws IOException
* If any I/O exception occurred.
*/
public static File zipDirectory(File dir2zip, File zippedFile) throws IOException {
try (ZipOutputStream zoStream = new ZipOutputStream(new FileOutputStream(zippedFile))) {
// open compressed output stream
// add output zip file to exclusions
File[] excludeFiles = new File[1];
excludeFiles[0] = zippedFile;
zipDirectory(dir2zip, zoStream, dir2zip, excludeFiles);
}
return zippedFile;
}
/**
* Zips the contents of a given directory to a given ZIP output stream. Paths of file entries in
* the ZIP stream are taken relatively to a given reference directory. If the reference directory
* is <code>null</code>, the file paths are taken relatively to the given directory to be zipped.
* The method allows to specify the list of files (or dirs) that should not be zipped.
*
* @param dir2zip
* The given directory to be zipped.
* @param zoStream
* The given ZIP output stream.
* @param referenceDir
* The given reference directory or <code>null</code>.
* @param excludeFiles
* The given list of files (or dirs) that should not be zipped.
* @return The ZIP output stream.
* @throws IOException
* If any I/O exception occurred.
*/
public static ZipOutputStream zipDirectory(File dir2zip, ZipOutputStream zoStream,
File referenceDir, File[] excludeFiles) throws IOException {
byte[] block = new byte[4096];
int inBytes = 0;
// get list of all files/dirs in the given directory
File[] dirFileList = dir2zip.listFiles();
// compress all files and sub-dirs
for (int i = 0; i < dirFileList.length; i++) {
File entry = dirFileList[i];
// check if this entry is not in the list of exclusions
boolean isExcluded = false;
for (int n = 0; n < excludeFiles.length; n++) {
if (entry.equals(excludeFiles[n])) {
isExcluded = true;
break;
}
}
if (isExcluded) {
continue;
}
// for each file - add ZipEntry and compress the file
if (entry.isFile()) {
// open input stream
try (FileInputStream iStream = new FileInputStream(entry)) {
// put ZipEntry for the file
String zipEntryName = (referenceDir != null)
? getRelativePath(referenceDir, entry.getAbsolutePath())
: getRelativePath(dir2zip, entry.getAbsolutePath());
ZipEntry zipEntry = new ZipEntry(zipEntryName);
zoStream.putNextEntry(zipEntry);
// read input stream and write to output stream
while ((inBytes = iStream.read(block)) > 0) {
zoStream.write(block, 0, inBytes);
}
}
} else if (entry.isDirectory()) { // zip sub-dir recursively
zipDirectory(entry, zoStream, referenceDir, excludeFiles);
}
}
return zoStream;
}
/**
* Zips a given file. The output ZIP file, by default, is created in the same directory, as the
* given input file, and has the same name, as the given input file with 'zip' extension.
*
* @param file2zip
* The file to be zipped.
* @return The output ZIP file.
* @throws IOException
* If any I/O exception occurred.
*/
public static File zipFile(File file2zip) throws IOException {
// construct zipped file path
String zipFileName = file2zip.getName();
int extIndex = zipFileName.lastIndexOf(DOT);
zipFileName = (extIndex >= 0) ? zipFileName.substring(0, extIndex) + ZIP_EXTENSION
: zipFileName + ZIP_EXTENSION;
File zipFile = new File(file2zip.getParentFile(), zipFileName);
return zipFile(file2zip, zipFile);
}
/**
* Zips a given file to a given output ZIP file.
*
* @param file2zip
* The file to be zipped.
* @param zippedFile
* The given output ZIP file.
* @return The output ZIP file.
* @throws IOException
* If any I/O exception occurred.
*/
public static File zipFile(File file2zip, File zippedFile) throws IOException {
byte[] block = new byte[4096];
int inBytes = 0;
try (FileInputStream iStream = new FileInputStream(file2zip);
ZipOutputStream oStream = new ZipOutputStream(new FileOutputStream(zippedFile));) {
// create ZipEntry, using input file name
ZipEntry zipEntry = new ZipEntry(file2zip.getName());
// add new ZipEntry
oStream.putNextEntry(zipEntry);
// read input stream and write to output stream
while ((inBytes = iStream.read(block)) > 0) {
oStream.write(block, 0, inBytes);
}
}
return zippedFile;
}
}