package betsy.common.tasks;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import betsy.common.timeouts.timeout.TimeoutRepository;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.tools.ant.taskdefs.Move;
import org.apache.tools.ant.taskdefs.Replace;
public class FileTasks {
private static final Logger LOGGER = Logger.getLogger(FileTasks.class);
public static void createFile(Path file, String content) {
mkdirs(file.getParent());
// normalize string to unix line breaks
content = content.replaceAll("\\r\\n", "\n");
String[] lines = content.split("\n");
String showOutput = Integer.toString(lines.length) + " lines";
if (lines.length < 5) {
showOutput = content.replace("\n", "@LINE_BREAK@");
}
String message = "Creating file " + file.toAbsolutePath() + " with " + showOutput;
LOGGER.info(message);
try {
Files.write(file, Arrays.asList(lines), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(message, e);
}
}
public static void deleteDirectory(Path directory) {
LOGGER.info("Deleting directory " + directory.toAbsolutePath());
if (Files.isDirectory(directory)) {
if (!FileUtils.deleteQuietly(directory.toAbsolutePath().toFile())) {
LOGGER.info("Deletion failed -> retrying after short wait");
WaitTasks.sleep(TimeoutRepository.getTimeout("FileTasks.deleteDirectory").getTimeoutInMs());
try {
FileUtils.deleteDirectory(directory.toAbsolutePath().toFile());
} catch (FileNotFoundException ignore) {
LOGGER.info("File " + directory.toAbsolutePath().toFile() + " could not be found. Ignoring deletion", ignore);
} catch (IOException e) {
LOGGER.info("Retry of deletion also failed.");
throw new IllegalStateException("could not delete directory " + directory, e);
}
}
} else {
LOGGER.info("Directory is already deleted!");
}
}
public static void mkdirs(Path dir) {
try {
LOGGER.info("Creating directory " + dir.toAbsolutePath());
if (Files.isDirectory(dir)) {
LOGGER.info("Directory already there - skipping creation");
} else {
Files.createDirectories(dir);
}
} catch (IOException e) {
throw new IllegalStateException("could not create directory " + dir.toAbsolutePath(), e);
}
}
public static void assertDirectory(Path dir) {
if (!Files.isDirectory(dir)) {
throw new IllegalArgumentException("the path " + dir.toAbsolutePath() + " is no directory");
}
}
public static void assertExecutableFile(Path p) {
assertFile(p);
if (!Files.isExecutable(p)) {
throw new IllegalArgumentException("the file " + p.toAbsolutePath() + " is not executable but should be executed");
}
}
public static void assertFile(Path p) {
if (!Files.isRegularFile(p)) {
throw new IllegalArgumentException("the file " + p + " is no file");
}
}
public static void assertFileExtension(Path path, String extension) {
assertFile(path);
if (!path.getFileName().toString().endsWith(extension)) {
throw new IllegalArgumentException("the file " + path + " does not have the extension " + extension);
}
}
public static List<String> readAllLines(Path path) {
try {
return Files.readAllLines(path, StandardCharsets.UTF_8);
} catch (IOException e) {
try {
return Files.readAllLines(path, StandardCharsets.ISO_8859_1);
} catch (IOException e1) {
throw new RuntimeException("could not read file with either ISO 8859 1 or UTF 8" + path, e1);
}
}
}
public static void deleteLine(Path path, int lineNumber) {
LOGGER.info("Deleting line #" + lineNumber + " from file " + path.toAbsolutePath());
try {
// line numbers start with 1, not with 0!
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
LOGGER.info("File has " + lines.size() + " lines -> removing #" + lineNumber + " with content " + lines.get(lineNumber - 1));
lines.remove(lineNumber - 1);
Files.write(path, lines, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Could not delete line #" + lineNumber + " in file " + path, e);
}
}
public static void replaceLine(Path path, String oldContent, String newLineContent) {
LOGGER.info("Replacing contents of a line which has exactly " + oldContent + " from file " + path.toAbsolutePath() + " with the new content " + newLineContent);
try {
// line numbers start with 1, not with 0!
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
LOGGER.info("File has " + lines.size() + " lines");
int index = findIndex(oldContent, lines);
if (index == -1) {
throw new IllegalStateException("Could not find a line with content [" + oldContent + "] in file " + path);
}
lines.set(index, newLineContent);
Files.write(path, lines, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Could not replace line in file " + path, e);
}
}
private static int findIndex(String oldContent, List<String> lines) {
int line = -1;
for (int i = 0; i < lines.size(); i++) {
if (lines.get(i).trim().equals(oldContent)) {
line = i;
}
}
return line;
}
public static void deleteFile(Path file) {
LOGGER.info("Deleting file " + file.toAbsolutePath());
if (!Files.isRegularFile(file)) {
LOGGER.info("File does not exist - must not be deleted");
return;
}
try {
Files.delete(file);
} catch (IOException e) {
throw new IllegalStateException("Could not delete file " + file, e);
}
}
public static void copyFileIntoFolder(Path file, Path targetFolder) {
assertFile(file);
assertDirectory(targetFolder);
try {
LOGGER.info("Copying file " + file.toAbsolutePath() + " into folder " + targetFolder.toAbsolutePath());
Files.copy(file, targetFolder.resolve(file.getFileName()));
} catch (IOException e) {
throw new IllegalStateException("Could not copy file " + file + " into folder " + targetFolder, e);
}
}
public static void copyFileIntoFolderAndOverwrite(Path file, Path targetFolder) {
assertFile(file);
assertDirectory(targetFolder);
try {
LOGGER.info("Copying file " + file.toAbsolutePath() + " into folder " + targetFolder.toAbsolutePath());
Files.copy(file, targetFolder.resolve(file.getFileName()), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new IllegalStateException("Could not copy file " + file + " into folder " + targetFolder, e);
}
}
public static void copyFileIntoFolderAndOverwrite(URL url, String filename, Path targetFolder) {
assertDirectory(targetFolder);
try {
LOGGER.info("Copying file " + url + " of " + filename + " into folder " + targetFolder.toAbsolutePath());
try(InputStream in = url.openStream()) {
Files.copy(in, targetFolder.resolve(filename), StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new IllegalStateException("Could not copy file " + url + " of " + filename + " into folder " + targetFolder, e);
}
}
public static void copyFileContentsToNewFile(Path source, Path target) {
assertFile(source);
try {
LOGGER.info("Copying contents of file " + source.toAbsolutePath() + " to file " + target.toAbsolutePath());
List<String> lines = Files.readAllLines(source);
mkdirs(target.getParent());
Files.write(target, lines, StandardCharsets.UTF_8);
LOGGER.info("Copying contents of file " + source.toAbsolutePath() + " to file " + target.toAbsolutePath() + " successful");
} catch (IOException e) {
throw new IllegalStateException("Could not copy contents of file " + source + " to file " + target, e);
}
}
public static boolean hasFile(Path file) {
LOGGER.info("Checking for the existence of file " + file.toAbsolutePath());
return Files.isRegularFile(file);
}
public static boolean hasFolder(Path folder) {
LOGGER.info("Checking for the existence of folder " + folder.toAbsolutePath());
return Files.isDirectory(folder);
}
public static boolean hasNoFile(Path file) {
LOGGER.info("Checking for the absence of file " + file.toAbsolutePath());
return !Files.isRegularFile(file);
}
public static boolean hasFileSpecificSubstring(Path path, String substring) {
LOGGER.info("Searching for substring " + substring + " in file " + path.toAbsolutePath());
try {
assertFile(path);
List<String> lines = readAllLines(path);
for (String line : lines) {
if (line.contains(substring)) {
return true;
}
}
} catch (Exception e) {
LOGGER.info("Could not read file " + path + " because " + e.getMessage());
}
return false;
}
public static void move(Path from, Path to) {
LOGGER.info("Moving file " + from + " to " + to);
try {
Files.move(from, to);
} catch (IOException e) {
throw new IllegalStateException("Could not move file " + from + " to " + to, e);
}
}
public static void moveFolderWithForcedCleanup(Path from, Path to) {
LOGGER.info("Moving file " + from + " to " + to);
Move move = new Move();
move.setFile(from.toFile());
move.setTofile(to.toFile());
move.setForce(true);
move.setPerformGcOnFailedDelete(true);
move.setTaskName("move");
move.setProject(AntUtil.builder().getProject());
move.execute();
}
public static Path findFirstMatchInFolder(Path folder, String glob) {
LOGGER.info("Finding first file in dir [" + folder + "] with pattern [" + glob + "]");
try {
FileTasks.assertDirectory(folder);
} catch (Exception ignore) {
return null;
}
try {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(folder, glob)) {
return stream.iterator().next();
}
} catch (IOException e) {
throw new RuntimeException("could not iterate in folder " + folder, e);
}
}
public static List<Path> findAllInFolder(Path folder) {
LOGGER.info("Finding all files in dir [" + folder + "]");
List<Path> result = new LinkedList<>();
try {
FileTasks.assertDirectory(folder);
} catch (Exception ignore) {
return result;
}
try {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(folder)) {
for (Path p : stream) {
result.add(p);
}
}
return result;
} catch (IOException e) {
throw new RuntimeException("could not iterate in folder " + folder, e);
}
}
public static List<Path> findAllInFolder(Path folder, String glob) {
LOGGER.info("Finding all files in dir [" + folder + "] with pattern [" + glob + "]");
List<Path> result = new LinkedList<>();
try {
FileTasks.assertDirectory(folder);
} catch (Exception ignore) {
return result;
}
try {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(folder, glob)) {
for (Path p : stream) {
result.add(p);
}
}
return result;
} catch (IOException e) {
throw new RuntimeException("could not iterate in folder " + folder, e);
}
}
public static void copyFilesInFolderIntoOtherFolder(Path from, Path to) {
LOGGER.info("Copying files from " + from + " to folder " + to);
assertDirectory(from);
assertDirectory(to);
try (DirectoryStream<Path> stream = Files.newDirectoryStream(from)) {
for (Path path : stream) {
if (Files.isRegularFile(path)) {
FileTasks.copyFileIntoFolder(path, to);
}
}
} catch (IOException e) {
throw new RuntimeException("Could not copy files from " + from + " to " + to, e);
}
}
public static void replaceTokensInFile(Path targetFile, Map<String, ?> replacements) {
LOGGER.info("Replacing tokens in " + targetFile.toAbsolutePath() + " {" + new PrettyPrintingMap<>(replacements).toString() + "}");
assertFile(targetFile);
Replace replaceTask = new Replace();
replaceTask.setFile(targetFile.toFile());
for (Map.Entry<String, ?> stringEntry : replacements.entrySet()) {
String value = String.valueOf(stringEntry.getValue());
Replace.Replacefilter filter = replaceTask.createReplacefilter();
filter.setToken(stringEntry.getKey());
filter.setValue(value);
}
replaceTask.setTaskName("replace");
replaceTask.setProject(AntUtil.builder().getProject());
replaceTask.validateReplacefilters();
replaceTask.execute();
}
public static void replaceTokensInFolder(Path targetFile, String token, Object replacement) {
Map<String, Object> map = new HashMap<>();
map.put(token, replacement);
replaceTokensInFolder(targetFile, map);
}
public static void replaceTokensInFolder(Path targetFile, Map<String, ?> replacements) {
LOGGER.info("Replacing tokens in " + targetFile.toAbsolutePath() + " {" + new PrettyPrintingMap<>(replacements).toString() + "}");
assertDirectory(targetFile);
Replace replaceTask = new Replace();
replaceTask.setDir(targetFile.toFile());
for (Map.Entry<String, ?> stringEntry : replacements.entrySet()) {
String value = String.valueOf(stringEntry.getValue());
Replace.Replacefilter filter = replaceTask.createReplacefilter();
filter.setToken(stringEntry.getKey());
filter.setValue(value);
}
replaceTask.setTaskName("replace");
replaceTask.setProject(AntUtil.builder().getProject());
replaceTask.validateReplacefilters();
replaceTask.execute();
}
public static void replaceTokenInFile(Path targetFile, String token, String value) {
Map<String, String> replacements = new HashMap<>();
replacements.put(token, value);
replaceTokensInFile(targetFile, replacements);
}
public static String getFilenameWithoutExtension(Path file) {
assertFile(file);
String filename = file.getFileName().toString();
return getFilenameWithoutExtension(filename);
}
public static String getFilenameWithoutExtension(String filename) {
String[] elements = filename.split("\\.");
if (elements.length == 1) {
return elements[0]; // no . in filename
}
StringJoiner joiner = new StringJoiner(".");
for (int i = 0; i < elements.length - 1; i++) {
joiner.add(elements[i]);
}
return joiner.toString();
}
public static String getFileExtension(Path filename) {
String[] elements = filename.toString().split("\\.");
if (elements.length == 1) {
return ""; // no . in filename
}
return elements[elements.length - 1];
}
public static void copyMatchingFilesIntoFolder(Path from, Path to, String globPattern) {
assertDirectory(from);
mkdirs(to);
try (DirectoryStream<Path> paths = Files.newDirectoryStream(from, globPattern)) {
for (Path path : paths) {
copyFileIntoFolder(path, to);
}
} catch (IOException e) {
throw new RuntimeException("Could not copy files [" + globPattern + "] from " + from + " to " + to);
}
}
public static void replaceLogFileContent(List<String> listToReplace, String replacement, Path logPath) {
LOGGER.info("Check if content of " + logPath.getFileName().toString() + " needs to be replaced.");
List<String> allLinesOfLogFile = readAllLines(logPath);
if (allLinesOfLogFile.removeAll(listToReplace)) {
try (PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(logPath))) {
allLinesOfLogFile.forEach(printWriter::println);
printWriter.println(replacement);
LOGGER.info("Replaced " + listToReplace.toString() + " with " + replacement + " in " + logPath.getFileName().toString() + ".");
} catch (IOException e) {
LOGGER.warn("Error while writing to " + logPath.getFileName() + ".");
}
}
}
}