FileUtils.java

package io.github.giulong.spectrum.utils;

import io.github.giulong.spectrum.enums.Frame;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

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.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 static java.util.Comparator.reverseOrder;
import static java.util.UUID.randomUUID;
import static lombok.AccessLevel.PRIVATE;

@Slf4j
@NoArgsConstructor(access = PRIVATE)
public final class FileUtils {

    private static final FileUtils INSTANCE = new FileUtils();
    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));
    }

    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.exists(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 String content) {
        final boolean foldersCreated = path.getParent().toFile().mkdirs();
        log.trace("Folders created? {}. Writing {} to file {}", foldersCreated, content, path);

        Files.write(path, content.getBytes());
    }

    public void write(final String path, final String content) {
        write(Path.of(path), content);
    }

    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();
    }

    public String getScreenshotNameFrom(final Frame frame, final StatefulExtentTest statefulExtentTest) {
        return String.format("%s-%s-%s.png", frame.getValue(), statefulExtentTest.getDisplayName(), randomUUID());
    }
}