FileUtils.java
package io.github.giulong.spectrum.utils;
import static java.util.Comparator.reverseOrder;
import static lombok.AccessLevel.PRIVATE;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@NoArgsConstructor(access = PRIVATE)
public final class FileUtils {
private static final FileUtils INSTANCE = new FileUtils();
public static final String HASH_ALGORITHM = "SHA-256";
private static final String DEFAULT_TIMESTAMP_PATTERN = "dd-MM-yyyy_HH-mm-ss";
private static final String TIMESTAMP_TO_REPLACE = "\\$\\{timestamp:?(?<pattern>.*)}";
private static final Pattern TIMESTAMP_PATTERN = Pattern.compile(".*\\$\\{timestamp:(?<pattern>.*)}.*");
private static final int[] ILLEGAL_CHARS = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 34, 42, 47,
58, 60, 62, 63, 92, 124};
public static FileUtils getInstance() {
return INSTANCE;
}
public String read(final String file) {
log.debug("Reading file {}", file);
final InputStream inputStream = FileUtils.class.getResourceAsStream(String.format("/%s", file));
if (inputStream == null) {
log.warn("File {} not found.", file);
return "";
}
try (Scanner scanner = new Scanner(inputStream)) {
return scanner.useDelimiter("\\Z").next();
}
}
public String readTemplate(final String file) {
return read(String.format("templates/%s", file));
}
@SneakyThrows
public byte[] readBytesOf(final Path path) {
return Files.readAllBytes(path);
}
public String interpolateTimestampFrom(final String value) {
final Matcher matcher = TIMESTAMP_PATTERN.matcher(value);
final String pattern = matcher.matches() ? matcher.group("pattern") : DEFAULT_TIMESTAMP_PATTERN;
return value.replaceAll(TIMESTAMP_TO_REPLACE, LocalDateTime.now().format(DateTimeFormatter.ofPattern(pattern)));
}
public String getExtensionOf(final String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
public String removeExtensionFrom(final String fileName) {
return fileName.replaceFirst("\\.[^.]*$", "");
}
@SneakyThrows
public Path delete(final Path path) {
if (Files.notExists(path)) {
log.debug("Avoid deleting non-existing path '{}'", path);
return path;
}
log.debug("About to delete path '{}'", path);
try (Stream<Path> files = Files.walk(path)) {
files
.sorted(reverseOrder())
.map(Path::toFile)
.forEach(f -> log.trace("File '{}' deleted? {}", f, f.delete()));
}
return path;
}
public Path delete(final File file) {
return delete(file.toPath());
}
@SneakyThrows
public Path deleteContentOf(final Path directory) {
return Files.createDirectories(delete(directory));
}
@SneakyThrows
public void write(final Path path, final byte[] content) {
final boolean foldersCreated = path.getParent().toFile().mkdirs();
log.trace("Folders created? {}. Writing {} to file {}", foldersCreated, content, path);
Files.write(path, content);
}
public void write(final Path path, final String content) {
write(path, content.getBytes());
}
public void write(final String path, final String content) {
write(Path.of(path), content.getBytes());
}
public String sanitize(final String name) {
final StringBuilder stringBuilder = new StringBuilder();
final int charLength = name.codePointCount(0, name.length());
for (int i = 0; i < charLength; i++) {
final int c = name.codePointAt(i);
if (Arrays.binarySearch(ILLEGAL_CHARS, c) < 0) {
stringBuilder.appendCodePoint(c);
}
}
return stringBuilder.toString();
}
@SneakyThrows
public FileTime getCreationTimeOf(final File file) {
return Files.readAttributes(file.toPath(), BasicFileAttributes.class).creationTime();
}
@SneakyThrows
public Path createTempFile(final String prefix, final String suffix) {
final Path path = Files.createTempFile(prefix, suffix);
path.toFile().deleteOnExit();
log.debug("Created file {}", path);
return path;
}
public String getScreenshotNameFrom(final TestData testData) {
return String.format("screenshot-%d.png", testData.getScreenshotNumber());
}
public String getFailedScreenshotNameFrom(final TestData testData) {
return String.format("screenshot-%d-failed.png", testData.getScreenshotNumber());
}
public String getScreenshotsDiffNameFrom(final TestData testData) {
return String.format("screenshot-%d-diff.png", testData.getScreenshotNumber());
}
@SneakyThrows
public byte[] checksumOf(final byte[] bytes) {
final byte[] digest = MessageDigest.getInstance(HASH_ALGORITHM).digest(bytes);
log.trace("{} is '{}'", HASH_ALGORITHM, Arrays.toString(digest));
return digest;
}
public boolean compare(final byte[] bytes1, final byte[] bytes2) {
return Arrays.equals(checksumOf(bytes1), checksumOf(bytes2));
}
public boolean compare(final Path path1, final Path path2) {
log.info("""
Checking if these files are the same:
{}
{}
""", path1, path2);
return compare(readBytesOf(path1), readBytesOf(path2));
}
public boolean compare(final Path path, final byte[] bytes) {
return compare(readBytesOf(path), bytes);
}
}