/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2015 Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.broad.igv.util;
import org.apache.commons.io.output.StringBuilderWriter;
import org.broad.igv.util.ftp.FTPUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Logger;
import org.broad.igv.Globals;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.*;
import java.util.regex.Pattern;
/**
* @author jrobinso
*/
public class FileUtils {
private static final Logger log = Logger.getLogger(FileUtils.class);
final public static String LINE_SEPARATOR = System.getProperty("line.separator");
final public static String FILE_SEP = System.getProperty("file.separator");
final static String[] igvJnlpPrefixes = {"igv", "ichip", "29mammals", "hic"};
public static boolean resourceExists(String path) {
if(path == null) return false;
try {
if (isRemote(path)) {
return HttpUtils.getInstance().resourceAvailable(new URL(path));
} else {
return (new File(path)).exists();
}
} catch (IOException e) {
log.error("Error checking existence of: " + path, e);
return false;
}
}
public static boolean isRemote(String path) {
if (path == null) {
return false;
}
return path.startsWith("http://") || path.startsWith("https://") ||
path.startsWith("ftp://") || path.startsWith("gs://");
}
public static boolean canWriteTo(File file) {
FileOutputStream fos = null;
try {
file.createNewFile();
return true;
// Has permission
} catch (Exception e) {
return false;
} finally {
file.delete();
}
}
public static String getInstallDirectory() {
String path = FileUtils.class
.getProtectionDomain()
.getCodeSource()
.getLocation().getPath();
File f = new File(path);
if (f.isDirectory()) {
return f.getAbsolutePath();
} else {
return f.getParentFile().getAbsolutePath();
}
}
/**
* Get the relative path from one file to another, specifying the directory separator.
* If one of the provided resources does not exist, it is assumed to be a file unless it ends with '/' or
* '\'.
*
* @param targetPath targetPath is calculated to this file
* @param basePath basePath is calculated from this file
* @param pathSeparator directory separator. The platform default is not assumed so that we can test Unix behaviour when running on Windows (for example)
* @return
*/
public static String getRelativePath(String basePath, String targetPath, String pathSeparator) {
// Normalize the paths
String normalizedTargetPath = FilenameUtils.normalizeNoEndSeparator(targetPath);
String normalizedBasePath = FilenameUtils.normalizeNoEndSeparator(basePath);
// Undo the changes to the separators made by normalization
if (pathSeparator.equals("/")) {
normalizedTargetPath = FilenameUtils.separatorsToUnix(normalizedTargetPath);
normalizedBasePath = FilenameUtils.separatorsToUnix(normalizedBasePath);
} else if (pathSeparator.equals("\\")) {
normalizedTargetPath = FilenameUtils.separatorsToWindows(normalizedTargetPath);
normalizedBasePath = FilenameUtils.separatorsToWindows(normalizedBasePath);
} else {
throw new IllegalArgumentException("Unrecognised dir separator '" + pathSeparator + "'");
}
String[] base = normalizedBasePath.split(Pattern.quote(pathSeparator));
String[] target = normalizedTargetPath.split(Pattern.quote(pathSeparator));
// First get all the common elements. Store them as a string,
// and also count how many of them there are.
StringBuffer common = new StringBuffer();
int commonIndex = 0;
while (commonIndex < target.length && commonIndex < base.length
&& target[commonIndex].equals(base[commonIndex])) {
common.append(target[commonIndex] + pathSeparator);
commonIndex++;
}
if (commonIndex == 0) {
// No single common path element. This most
// likely indicates differing drive letters, like C: and D:.
// These paths cannot be relativized.
return targetPath;
}
// The number of directories we have to backtrack depends on whether the base is a file or a dir
// For example, the relative path from
//
// /foo/bar/baz/gg/ff to /foo/bar/baz
//
// ".." if ff is a file
// "../.." if ff is a directory
//
// The following is a heuristic to figure out if the base refers to a file or dir. It's not perfect, because
// the resource referred to by this path may not actually exist, but it's the best I can do
boolean baseIsFile = true;
File baseResource = new File(normalizedBasePath);
if (baseResource.exists()) {
baseIsFile = baseResource.isFile();
} else if (basePath.endsWith(pathSeparator)) {
baseIsFile = false;
}
StringBuffer relative = new StringBuffer();
if (base.length != commonIndex) {
int numDirsUp = baseIsFile ? base.length - commonIndex - 1 : base.length - commonIndex;
for (int i = 0; i < numDirsUp; i++) {
relative.append(".." + pathSeparator);
}
}
relative.append(normalizedTargetPath.substring(common.length()));
return relative.toString();
}
static public String getPlatformIndependentPath(String path) {
return path.replace('\\', '/');
}
// Convenience method
public static String getRelativePath(String basePath, String targetPath) {
return getRelativePath(basePath, targetPath, FILE_SEP);
}
/**
* Delete a directory and all subdirectories
*
* @param dir
* @return
*/
public static boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
boolean success = deleteDir(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
/**
* Test to see if the first comment line (first line not starting with #) is tab-delimited with the
* given number of minimum columns. Limit the test to the first 1,000 lines.
*/
public static boolean isTabDelimited(ResourceLocator loc, int minColumnCount) throws IOException {
BufferedReader reader = null;
try {
reader = ParsingUtils.openBufferedReader(loc.getPath());
int nLinesTested = 0;
String nextLine;
while ((nextLine = reader.readLine()) != null && nLinesTested < 1000) {
if (nextLine.startsWith("#")) {
continue;
}
nLinesTested++;
String[] tokens = nextLine.split("\t");
if (tokens.length >= minColumnCount) {
return true;
}
}
return false;
} finally {
if (reader != null) {
reader.close();
}
}
}
/**
* Copy a file from one location to another, using buffered writing
*
* @param inputFile
* @param outputFile
* @throws java.io.IOException
*/
public static void copyFile(File inputFile, File outputFile) throws IOException {
OutputStream out = null;
InputStream in = null;
try {
in = new FileInputStream(inputFile);
out = new FileOutputStream(outputFile);
byte[] buffer = new byte[64000];
int bytes_read;
while ((bytes_read = in.read(buffer)) != -1) {
out.write(buffer, 0, bytes_read);
}
} catch (Exception e) {
outputFile.delete();
throw new RuntimeException("<html>Error copying file: " + outputFile.getAbsoluteFile() +
"<br/>" + e.toString());
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.flush();
out.close();
}
}
}
private static void cleanup(File dir) {
if (dir.exists() && dir.isDirectory()) {
File[] jnlpFiles = dir.listFiles(new FileFilter() {
public boolean accept(File arg0) {
final String name = arg0.getName();
for (String pre : igvJnlpPrefixes) {
if (name.startsWith(pre) && name.endsWith(".jnlp")) {
return true;
}
}
return false;
}
});
// Sort files by ascending version number
Arrays.sort(jnlpFiles, new Comparator<File>() {
public int compare(File file1, File file2) {
if (org.apache.commons.io.FileUtils.isFileNewer(file1, file2)) {
return 1;
} else {
return -1;
}
}
});
// Delete all but the highest version (newest) jnlp file
for (int i = 0; i < jnlpFiles.length - 1; i++) {
jnlpFiles[i].delete();
}
// Strip the version nuber fro the newest file
if (jnlpFiles.length > 1) {
File newestFile = jnlpFiles[jnlpFiles.length - 1];
String fn = newestFile.getName();
int dotIndex = fn.indexOf(".jnlp");
int dashIndex = fn.lastIndexOf("-");
if (dashIndex > 1) {
String newName = fn.substring(0, dashIndex) + fn.substring(dotIndex);
newestFile.renameTo(new File(newestFile.getParentFile(), newName));
}
}
}
}
// public static void main(String[] args) throws IOException {
// File inputDirectory = new File(args[0]);
// File outputDirectory = new File(args[1]);
// searchAndReplace(inputDirectory, outputDirectory, args[2], args[3]);
// }
public static String getFileExtension(String filePath) {
String extension = null;
int indexOfExtension = filePath.lastIndexOf(".");
if (indexOfExtension >= 0) {
extension = filePath.substring(indexOfExtension, filePath.length());
}
return extension;
}
/**
* Returns an absolute path from the parent directory
* of {@code referencePath} to the sub-element {@code inputPath}.
* Safe to use with URLs.
* If {@code inputPath} is an absolute path, it is returned unaltered.
* <br/>
* e.g.<br/>
* String absPath = FileUtils.getAbsolutePath("test/mysession.xml", "/Users/bob/data/otherdata.xml");
* System.out.println(absPath);<br/>
* >>>> /Users/bob/data/test/mysession.xml
*
* @param inputPath Relative path element
* @param referencePath Absolute path root
* @return
*/
public static String getAbsolutePath(String inputPath, String referencePath) {
if (isRemote(inputPath)) {
return inputPath;
}
File inFile = new File(inputPath);
if (inFile.isAbsolute()) {
return inFile.getAbsolutePath();
}
String absolutePath;
if (isRemote(referencePath)) {
int idx = referencePath.lastIndexOf("/");
String basePath = referencePath.substring(0, idx);
absolutePath = basePath + "/" + inputPath;
} else {
File referenceFile = new File(referencePath);
File parent = referenceFile.isDirectory() ? referenceFile : referenceFile.getParentFile();
File file = new File(parent, inputPath);
try {
absolutePath = file.getCanonicalPath();
} catch (IOException e) {
absolutePath = file.getAbsolutePath();
}
}
return absolutePath;
}
/**
* Return the path path. The trailing "/" is not included.
*
* @param path
* @return
*/
public static String getParent(String path) {
String piPath = getPlatformIndependentPath(path);
int lastSlashIdx = piPath.lastIndexOf("/");
return lastSlashIdx <= 0 ? path : path.substring(0, lastSlashIdx);
}
/**
* Checks the system path for the provided executable.
* If {@code executable} is a path (contains a path separator)
* then it is returned unaltered
*
* @param executable
* @return
*/
public static String findExecutableOnPath(String executable) {
if (executable.contains(File.separator)) return executable;
String systemPath = System.getenv("PATH");
if (systemPath == null) systemPath = System.getenv("path");
if (systemPath == null || File.pathSeparator == null) return executable;
String[] pathDirs = systemPath.split(File.pathSeparator);
String fullPath = executable;
for (String pathDir : pathDirs) {
File file = new File(pathDir, executable);
if (file.isFile()) {
fullPath = file.getAbsolutePath();
break;
}
}
return fullPath;
}
/**
* Return the length of the file, which might be remote.
*
* @param file
* @return
*/
public static long getLength(String file) {
if (isRemote(file)) {
try {
URL url = new URL(file);
if (file.startsWith("ftp://")) {
return FTPUtils.getContentLength(url);
} else {
return HttpUtils.getInstance().getContentLength(url);
}
} catch (Exception e) {
log.error("Error fetching content length for: " + file, e);
return -1;
}
} else {
File f = new File(file);
if (f.exists() && f.isFile()) {
return f.length();
} else {
return -1;
}
}
}
/**
* Read and return the file contents as a string
* @param path
* @return
*/
public static String getContents(String path) throws IOException {
BufferedReader reader = ParsingUtils.openBufferedReader(path);
StringBuilder contents = new StringBuilder();
PrintWriter pw = new PrintWriter(new StringBuilderWriter(contents));
String nextLine;
while((nextLine = reader.readLine()) != null) {
pw.println(nextLine);
}
return contents.toString();
}
public static byte[] readFully(String aInputFileName) {
File file = new File(aInputFileName);
byte[] result = new byte[(int) file.length()];
try {
InputStream input = null;
try {
int totalBytesRead = 0;
input = new BufferedInputStream(new FileInputStream(file));
while (totalBytesRead < result.length) {
int bytesRemaining = result.length - totalBytesRead;
//input.read() returns -1, 0, or more :
int bytesRead = input.read(result, totalBytesRead, bytesRemaining);
if (bytesRead > 0) {
totalBytesRead = totalBytesRead + bytesRead;
}
}
} finally {
input.close();
}
} catch (FileNotFoundException ex) {
} catch (IOException ex) {
}
return result;
}
}