Inceptum/common/src/main/java/io/gitlab/jfronny/inceptum/util/Utils.java

276 lines
12 KiB
Java

package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.commons.*;
import io.gitlab.jfronny.commons.io.MultiAccessFileSystem;
import io.gitlab.jfronny.commons.log.Logger;
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
import io.gitlab.jfronny.inceptum.util.cache.GsonFileCache;
import java.awt.*;
import java.io.*;
import java.lang.reflect.Type;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;
public class Utils {
public static final Pattern VALID_FILENAME = Pattern.compile("[a-zA-Z0-9_\\-.][a-zA-Z0-9 _\\-.]*[a-zA-Z0-9_\\-.]");
public static final Logger LOGGER = Logger.forName("Inceptum");
private static ClassLoader SYSTEM_LOADER = ClassLoader.getSystemClassLoader();
private static final GsonFileCache OBJECT_CACHE = new GsonFileCache(MetaHolder.CACHE_DIR);
public static byte[] downloadData(String url) throws IOException, URISyntaxException {
try (InputStream is = HttpUtils.get(url).sendInputStream()) {
return is.readAllBytes();
}
}
public static byte[] downloadData(String url, String sha1) throws IOException, URISyntaxException {
byte[] buf = downloadData(url);
if (sha1 == null) return buf;
if (!HashUtils.sha1(buf).equals(sha1)) throw new IOException("Invalid hash");
return buf;
}
public static <T> T downloadObject(String url, Class<T> type) throws IOException {
return downloadObject(url, type, true);
}
public static <T> T downloadObject(String url, Class<T> type, boolean cache) throws IOException {
return downloadObject(url, () -> HttpUtils.get(url).sendString(), type, cache);
}
public static <T> T downloadObject(String url, Type type) throws IOException {
return downloadObject(url, type, true);
}
public static <T> T downloadObject(String url, Type type, String apiKey) throws IOException {
return downloadObject(url, () -> HttpUtils.get(url).header("x-api-key", apiKey).sendString(), type, true);
}
public static <T> T downloadObject(String url, Type type, boolean cache) throws IOException {
return downloadObject(url, () -> HttpUtils.get(url).sendString(), type, cache);
}
public static <T> T downloadObject(String url, String sha1, Class<T> type) throws IOException {
return downloadObject(url, sha1, type, true);
}
public static <T> T downloadObject(String url, String sha1, Class<T> type, boolean cache) throws IOException {
return downloadObject(url, () -> downloadString(url, sha1), type, cache);
}
private static <T> T downloadObject(String url, ThrowingSupplier<String, Exception> sourceString, Type type, boolean cache) throws IOException {
try {
ThrowingSupplier<T, Exception> builder = () -> GsonHolder.getGson().fromJson(sourceString.get(), type);
return cache ? OBJECT_CACHE.get(HashUtils.sha1(url.getBytes(StandardCharsets.UTF_8)), builder, type) : builder.get();
} catch (Exception e) {
throw new IOException("Could not download object and no cache exists", e);
}
}
public static String buildUrl(String host, String url, Map<String, String> params) {
StringBuilder res = new StringBuilder(host);
if (res.toString().endsWith("/")) res = new StringBuilder(res.substring(0, res.length() - 1));
if (url.startsWith("/")) res.append(url);
else res.append("/").append(url);
int i = 0;
for (Map.Entry<String, String> entry : params.entrySet()) {
res.append(i++ == 0 ? '?' : '&')
.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
.append('=')
.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
}
return res.toString();
}
public static <T> T loadObject(Path file, Class<T> type) throws IOException {
try (BufferedReader br = Files.newBufferedReader(file)) {
return GsonHolder.getGson().fromJson(br, type);
}
}
public static <T> T loadObject(Path file, Type type) throws IOException {
try (BufferedReader br = Files.newBufferedReader(file)) {
return GsonHolder.getGson().fromJson(br, type);
}
}
public static <T> void writeObject(Path file, T object) throws IOException {
try (BufferedWriter bw = Files.newBufferedWriter(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
GsonHolder.getGson().toJson(object, bw);
}
}
public static String downloadString(String url, String sha1) throws IOException, URISyntaxException {
return new String(downloadData(url, sha1), StandardCharsets.UTF_8);
}
public static void downloadFile(String url, Path path) throws IOException, URISyntaxException {
if (!Files.exists(path.getParent())) Files.createDirectories(path.getParent());
Files.write(path, downloadData(url));
}
public static void downloadFile(String url, String sha1, Path path) throws IOException, URISyntaxException {
if (!Files.exists(path.getParent())) Files.createDirectories(path.getParent());
Files.write(path, downloadData(url, sha1));
}
public static void clearDirectory(Path path) throws IOException {
clearDirectory(path, p -> true);
}
public static void clearDirectory(Path path, Predicate<Path> shouldDelete) throws IOException {
if (!Files.exists(path)) return;
try {
Utils.ls(path, p -> {
if (Files.isDirectory(p)) {
try {
if (shouldDelete.test(p))
deleteRecursive(p, shouldDelete);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
else {
try {
if (shouldDelete.test(p))
Files.delete(p);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
} catch (Throwable t) {
throw new IOException("Could not clear directory", t);
}
}
public static void deleteRecursive(Path path) throws IOException {
deleteRecursive(path, p -> true);
}
public static void deleteRecursive(Path path, Predicate<Path> shouldDelete) throws IOException {
if (Files.isDirectory(path)) {
Files.walkFileTree(path, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
FileVisitResult fv = super.visitFile(file, attrs);
if (fv != FileVisitResult.CONTINUE) return fv;
if (shouldDelete.test(file))
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
FileVisitResult fv = super.postVisitDirectory(dir, exc);
if (fv != FileVisitResult.CONTINUE) return fv;
if (shouldDelete.test(dir) && ls(dir).isEmpty()) {
Files.delete(dir);
}
return FileVisitResult.CONTINUE;
}
});
}
else Files.delete(path);
}
public static void copyContent(Path source, Path destination) throws IOException {
copyContent(source, destination, StandardCopyOption.COPY_ATTRIBUTES);
}
public static void copyContent(Path source, Path destination, CopyOption... copyOptions) throws IOException {
if (!Files.exists(destination)) Files.createDirectories(destination);
if (Files.isDirectory(source)) {
try (Stream<Path> paths = Files.walk(source)) {
for (Path path : paths.toList()) {
if (source.equals(path)) continue;
Path d = destination.resolve(source.relativize(path).toString());
if (Files.exists(d)) continue;
if (Files.isDirectory(d)) Files.createDirectories(d);
else Files.copy(path, d);
}
}
} else if (Files.exists(source)) {
Path target = destination.resolve(source.getFileName().toString());
if (!Files.exists(target)
|| Arrays.asList(copyOptions).contains(StandardCopyOption.REPLACE_EXISTING))
Files.copy(source, target, copyOptions);
} else {
throw new FileNotFoundException(source.toAbsolutePath().toString());
}
}
public static void openWebBrowser(URI uri) {
try {
if (OSUtils.TYPE == OSUtils.Type.LINUX && OSUtils.executablePathContains("xdg-open")) {
Runtime.getRuntime().exec(new String[]{"xdg-open", uri.toString()});
} else if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
Desktop.getDesktop().browse(uri);
}
} catch (Exception e) {
Utils.LOGGER.error("Error opening web browser!", e);
}
}
public static void openFile(File file) {
try {
if (OSUtils.TYPE == OSUtils.Type.LINUX && OSUtils.executablePathContains("xdg-open")) {
Runtime.getRuntime().exec(new String[]{"xdg-open", file.getAbsoluteFile().toString()});
} else if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
Desktop.getDesktop().open(file);
}
} catch (Exception e) {
Utils.LOGGER.error("Error opening web browser!", e);
}
}
public static List<Path> ls(Path dir) throws IOException {
try (Stream<Path> sp = Files.list(dir)) {
return sp.toList();
}
}
public static String[] lsVi(Path dir) throws IOException {
try (Stream<Path> sp = Files.list(dir)) {
return sp.map(p -> Files.isDirectory(p) ? p.getFileName().toString() + "/" : p.getFileName().toString()).toArray(String[]::new);
}
}
public static List<Path> ls(Path dir, Predicate<Path> predicate) throws IOException {
try (Stream<Path> sp = Files.list(dir); Stream<Path> fi = sp.filter(predicate)) {
return fi.toList();
}
}
public static <TEx extends Exception> void ls(Path dir, ThrowingConsumer<Path, TEx> consumer) throws IOException, TEx {
try (Stream<Path> sp = Files.list(dir)) {
for (Path path : sp.toList()) consumer.accept(path);
}
}
private static final Map<Path, MultiAccessFileSystem> zipFsCache = new HashMap<>();
public static FileSystem openZipFile(Path zip, boolean create) throws IOException, URISyntaxException {
synchronized (zipFsCache) {
if (!zipFsCache.containsKey(zip) || zipFsCache.get(zip).isClosed()) {
URI fileUri = zip.toUri();
zipFsCache.put(zip, MultiAccessFileSystem.create(new URI("jar:" + fileUri.getScheme(), fileUri.getPath(), null), create ? Map.of("create", "true") : Map.of(), SYSTEM_LOADER));
}
return zipFsCache.get(zip).createLens();
}
}
public static void setSystemLoader(ClassLoader loader) {
SYSTEM_LOADER = loader;
}
}