/*
* Copyright 2016 ThoughtWorks, Inc.
*
* 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.
*/
package com.thoughtworks.go.util;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
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.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.UUID;
import static java.lang.System.getProperty;
public class FileUtil {
private static final String CRUISE_TMP_FOLDER = "cruise" + "-" + UUID.randomUUID().toString();
private static final Logger LOGGER = Logger.getLogger(FileUtil.class);
private static final boolean ON_NETWARE = OperatingSystem.isFamily("netware");
private static final boolean ON_DOS = OperatingSystem.isFamily("dos");
public static boolean isFolderEmpty(File folder) {
if (folder == null) {
return true;
}
File[] files = folder.listFiles();
return files == null || files.length == 0;
}
public static boolean isDirectoryReadable(File directory) {
return directory.canRead() && directory.canExecute() && directory.listFiles() != null;
}
public static String makepath(String... paths) {
StringBuilder fullPath = new StringBuilder();
for (String path : paths) {
fullPath.append(path);
fullPath.append("/");
}
return fullPath.toString().substring(0, fullPath.length() - 1);
}
public static String readToEnd(File file) throws IOException {
FileInputStream input = new FileInputStream(file);
return readToEnd(input);
}
public static String readToEnd(InputStream input) throws IOException {
try {
@SuppressWarnings("unchecked") List<String> list = IOUtils.readLines(input);
StringBuilder builder = new StringBuilder();
for (String line : list) {
builder.append(line);
builder.append(lineSeparator());
}
return builder.toString().trim();
} finally {
IOUtils.closeQuietly(input);
}
}
public static boolean isHidden(File file) {
return file.isHidden() || file.getName().startsWith(".");
}
public static void writeContentToFile(String content, File file) throws IOException {
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(file);
IOUtils.write(content, outputStream);
} finally {
IOUtils.closeQuietly(outputStream);
}
}
public static void writeContentToFile(byte[] content, File file) throws IOException {
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(file);
IOUtils.write(content, outputStream);
} finally {
IOUtils.closeQuietly(outputStream);
}
}
public static String readContentFromFile(File file) throws IOException {
FileInputStream input = null;
try {
input = new FileInputStream(file);
return IOUtils.toString(input);
} finally {
IOUtils.closeQuietly(input);
}
}
public static boolean deleteFolder(File testFolder) {
return FileUtils.deleteQuietly(testFolder);
}
public static String normalizePath(File fileToNormalize) {
return normalizePath(fileToNormalize.getPath());
}
public static String normalizePath(String filePath) {
return StringUtils.replace(filePath, "\\", "/");
}
public static String fileNameFromPath(String src) {
String[] urlparts = normalizePath(src).split("/");
return urlparts[urlparts.length - 1];
}
public static String applyBaseDirIfRelativeAndNormalize(File baseDir, File actualFileToUse) {
return normalizePath(applyBaseDirIfRelative(baseDir, actualFileToUse));
}
public static File applyBaseDirIfRelative(File baseDir, File actualFileToUse) {
if (actualFileToUse == null) {
return baseDir;
}
if (actualFileToUse.isAbsolute()) {
return actualFileToUse;
}
if(StringUtil.isBlank(baseDir.getPath())) {
return actualFileToUse;
}
return new File(baseDir, actualFileToUse.getPath());
}
public static void validateAndCreateDirectory(File directory) {
if (directory.exists()) {
return;
}
try {
FileUtils.forceMkdir(directory);
} catch (IOException e) {
throw new RuntimeException("Failed to create folder: " + directory.getAbsolutePath());
}
}
public static void createParentFolderIfNotExist(File file) {
File parentFile = file.getParentFile();
if (parentFile != null && !parentFile.exists()) {
parentFile.mkdirs();
}
}
public static String lineSeparator() {
return getProperty("line.separator");
}
public static String fileseparator() {
return File.separator;
}
public static String toFileURI(File file) {
URI uri = file.toURI();
String uriString = uri.toASCIIString();
return uriString.replaceAll("^file:/", "file:///");
}
public static Boolean isStructureSame(File folder1, File folder2) {
List<String> structure1 = flatten(folder1);
List<String> structure2 = flatten(folder2);
Collections.sort(structure1);
Collections.sort(structure2);
return structure1.equals(structure2);
}
private static List<String> flatten(File folder1) {
ArrayList<String> list = new ArrayList<>();
flatten(list, folder1.getAbsolutePath(), folder1);
return list;
}
private static void flatten(List<String> list, String absPath, File file1) {
if (file1.isFile()) {
list.add(file1.getAbsolutePath().replace(absPath, ""));
} else if (file1.isDirectory()) {
for (File file : file1.listFiles()) {
flatten(list, absPath, file);
}
}
}
public static String filesystemSafeFileHash(File folder) {
String hash = StringUtil.sha1Digest(folder.getAbsolutePath().getBytes());
hash = hash.replaceAll("[^0-9a-zA-Z\\.\\-]", "");
return hash;
}
public static boolean isChildOf(File parent, File subdirectory) throws IOException {
File parentFile = parent.getCanonicalFile();
File current = subdirectory.getCanonicalFile();
return !current.equals(parentFile) && isSubdirectoryOf(parent, subdirectory);
}
public static boolean isSubdirectoryOf(File parent, File subdirectory) throws IOException {
File parentFile = parent.getCanonicalFile();
File current = subdirectory.getCanonicalFile();
while (current != null) {
if (current.equals(parentFile)) {
return true;
}
current = current.getParentFile();
}
return false;
}
//CopiedFromAnt
public static boolean isAbsolutePath(String filename) {
File file = new File(filename);
boolean absolute = file.isAbsolute();
if (absolute && OperatingSystem.isFamily(OperatingSystem.WINDOWS)) {
if (filename.startsWith("\\\\") && !filename.matches("\\\\\\\\.*\\\\.+")) {
absolute = false;
}
}
return absolute;
}
public static String[] dissect(String path) {
char sep = File.separatorChar;
path = path.replace('/', sep).replace('\\', sep);
// make sure we are dealing with an absolute path
if (!isAbsolutePath(path)) {
throw new RuntimeException(path + " is not an absolute path");
}
String root;
int colon = path.indexOf(':');
if (colon > 0 && (ON_DOS || ON_NETWARE)) {
int next = colon + 1;
root = path.substring(0, next);
char[] ca = path.toCharArray();
root += sep;
//remove the initial separator; the root has it.
next = (ca[next] == sep) ? next + 1 : next;
StringBuilder sbPath = new StringBuilder();
// Eliminate consecutive slashes after the drive spec:
for (int i = next; i < ca.length; i++) {
if (ca[i] != sep || ca[i - 1] != sep) {
sbPath.append(ca[i]);
}
}
path = sbPath.toString();
} else if (path.length() > 1 && path.charAt(1) == sep) {
// UNC drive
int nextsep = path.indexOf(sep, 2);
nextsep = path.indexOf(sep, nextsep + 1);
root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path;
path = path.substring(root.length());
} else {
root = File.separator;
path = path.substring(1);
}
return new String[]{root, path};
}
public static File normalize(final String path) {
Stack s = new Stack();
String[] dissect = dissect(path);
s.push(dissect[0]);
StringTokenizer tok = new StringTokenizer(dissect[1], File.separator);
while (tok.hasMoreTokens()) {
String thisToken = tok.nextToken();
if (".".equals(thisToken)) {
continue;
}
if ("..".equals(thisToken)) {
if (s.size() < 2) {
// Cannot resolve it, so skip it.
return new File(path);
}
s.pop();
} else { // plain component
s.push(thisToken);
}
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.size(); i++) {
if (i > 1) {
// not before the filesystem root and not after it, since root
// already contains one
sb.append(File.separatorChar);
}
sb.append(s.elementAt(i));
}
return new File(sb.toString());
}
public static String removeLeadingPath(File leading, File path) {
String l = normalize(leading.getAbsolutePath()).getAbsolutePath();
String p = normalize(path.getAbsolutePath()).getAbsolutePath();
if (l.equals(p)) {
return "";
}
// ensure that l ends with a /
// so we never think /foo was a parent directory of /foobar
if (!l.endsWith(File.separator)) {
l += File.separator;
}
return removeLeadingPath(l, p);
}
public static String removeLeadingPath(String leading, String path) {
if(StringUtil.isBlank(leading)) {
return path;
}
return (path.startsWith(leading)) ? path.substring(leading.length()) : path;
}
public static boolean isSymbolicLink(File parent, String name)
throws IOException {
if (parent == null) {
File f = new File(name);
parent = f.getParentFile();
name = f.getName();
}
File toTest = new File(parent.getCanonicalPath(), name);
return !toTest.getAbsolutePath().equals(toTest.getCanonicalPath());
}
public static void createFilesByPath(File baseDir, String... files) throws IOException {
for (String file : files) {
if (file.endsWith("/")) {
File file1 = new File(baseDir, file);
file1.mkdirs();
} else {
File file1 = new File(baseDir, file);
file1.getParentFile().mkdirs();
file1.createNewFile();
}
}
}
public static String subtractPath(File rootPath, File file) {
String fullPath = normalizePath(file.getParentFile());
String basePath = normalizePath(rootPath);
return StringUtils.removeStart(StringUtils.removeStart(fullPath, basePath), "/");
}
private static final long ONE_KB = 1024;
private static final long ONE_MB = ONE_KB * ONE_KB;
private static final long ONE_GB = ONE_KB * ONE_MB;
private static final long ONE_TB = ONE_KB * ONE_GB;
private static final long ONE_PB = ONE_KB * ONE_TB;
public static String byteCountToDisplaySize(long size) {
if (size >= ONE_PB) {
return displaySizeFor(size, ONE_PB, " PB");
} else if (size >= ONE_TB) {
return displaySizeFor(size, ONE_TB, " TB");
} else if (size >= ONE_GB) {
return displaySizeFor(size, ONE_GB, " GB");
} else if (size >= ONE_MB) {
return displaySizeFor(size, ONE_MB, " MB");
} else if (size >= ONE_KB) {
return displaySizeFor(size, ONE_KB, " KB");
} else {
return String.valueOf(size) + " bytes";
}
}
private static String displaySizeFor(long size, double unit, String unitInString) {
BigDecimal bigDecimal = new BigDecimal((double) size / unit);
return bigDecimal.setScale(1, RoundingMode.HALF_UP) + unitInString;
}
public static List<String> readLines(InputStream resource) throws IOException {
String output = readToEnd(resource);
return Arrays.asList(output.split("[\r\n]+"));
}
public static File createTempFolder() {
File tempDir = new File("data", CRUISE_TMP_FOLDER);
File dir = new File(tempDir, UUID.randomUUID().toString());
boolean ret = dir.mkdirs();
if (!ret) {
throw new RuntimeException("FileUtil#createTempFolder - Could not create temp folder");
}
return dir;
}
public static boolean isFolderInsideSandbox(String path) {
File fileAtPath = new File(path);
if (fileAtPath.isAbsolute()) {
return false;
}
try {
if (!FileUtil.isSubdirectoryOf(new File("."), fileAtPath)) {
return false;
}
} catch (IOException e) {
ExceptionUtils.bomb("Dest folder specification is not valid. " + e.getMessage());
}
return true;
}
public static void writeToFile(InputStream stream, File dest) throws IOException {
FileOutputStream fos = null;
try {
fos = FileUtils.openOutputStream(dest);
IOUtils.copy(stream, fos);
} finally {
IOUtils.closeQuietly(fos);
}
}
public static void tryDeleting(final File file) {
for (int i = 0; i < 10; i++) {
if (!file.exists()) {
return;
}
FileUtils.deleteQuietly(file);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static String getCanonicalPath(File workDir) {
try {
return workDir.getCanonicalPath();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void recreateDirectory(File dir) throws IOException {
FileUtils.deleteDirectory(dir);
dir.mkdirs();
}
public static void deleteDirectoryNoisily(File defaultDirectory) throws IOException {
if (!defaultDirectory.exists()) {
return;
}
try {
FileUtils.deleteDirectory(defaultDirectory);
} catch (IOException e) {
throw new RuntimeException("Failed to delete directory: " + defaultDirectory.getAbsolutePath(), e);
}
}
public static String join(File defaultWorkingDir, String actualFileToUse) {
if (actualFileToUse == null) {
LOGGER.trace("Using the default Directory->" + defaultWorkingDir);
return normalizePath(defaultWorkingDir);
}
return applyBaseDirIfRelativeAndNormalize(defaultWorkingDir, new File(actualFileToUse));
}
}