mirror of
https://github.com/AXOLOTsh/RegistryLib.git
synced 2026-06-02 09:46:33 +03:00
feature: add project files
This commit is contained in:
213
src/main/java/io/github/axolotsh/registrylib/RegistryLoader.java
Normal file
213
src/main/java/io/github/axolotsh/registrylib/RegistryLoader.java
Normal file
@@ -0,0 +1,213 @@
|
||||
package io.github.axolotsh.registrylib;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.key.Keyed;
|
||||
|
||||
/**
|
||||
* Provides utility methods for loading and saving {@link Keyed} registry
|
||||
* objects from the file system.
|
||||
*/
|
||||
@Builder
|
||||
@Accessors(chain = true, fluent = true)
|
||||
public class RegistryLoader {
|
||||
/**
|
||||
* The logger used to report IO errors and serialization failures.
|
||||
*
|
||||
* @return The current logger instance.
|
||||
*/
|
||||
@Getter
|
||||
private Logger logger;
|
||||
|
||||
/**
|
||||
* The base directory where registry files are stored.
|
||||
*
|
||||
* @return The current base directory.
|
||||
*/
|
||||
@Getter
|
||||
private Path rootPath;
|
||||
|
||||
private static final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Key.class, new KeyAdapter())
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
/**
|
||||
* Loads a single JSON file and converts it into a keyed object.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @param type The registry type category (e.g., "items", "quests").
|
||||
* @param classType The class to deserialize into.
|
||||
* @param <T> A type extending {@link Keyed}.
|
||||
* @return The loaded object, or {@code null} if loading
|
||||
* fails.
|
||||
*/
|
||||
@Nullable
|
||||
public <T extends Keyed> T loadFile(Path filePath, String type, Class<T> classType) {
|
||||
try (var reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
|
||||
var result = gson.fromJson(reader, classType);
|
||||
injectKey(result, getFileKey(filePath, type));
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to load registry file {}: {}", filePath, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void injectKey(Object target, Key key) throws Exception {
|
||||
var current = target.getClass();
|
||||
while (current != null) {
|
||||
try {
|
||||
var field = current.getDeclaredField("key");
|
||||
field.setAccessible(true);
|
||||
field.set(target, key);
|
||||
return;
|
||||
} catch (NoSuchFieldException e) {
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException("Field 'key' not found in class hierarchy");
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a keyed object to its corresponding JSON file location.
|
||||
*
|
||||
* @param value The object to save.
|
||||
* @param type The registry type category (e.g., "items", "quests").
|
||||
* @param <T> A type extending {@link Keyed}.
|
||||
*/
|
||||
public <T extends Keyed> void saveFile(T value, String type) {
|
||||
var filePath = getKeyFile(value.key(), type);
|
||||
try {
|
||||
Files.createDirectories(filePath.getParent());
|
||||
try (var writer = Files.newBufferedWriter(filePath, StandardCharsets.UTF_8)) {
|
||||
gson.toJson(value, writer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to save registry file {}: {}", filePath, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a {@link Key} based on a file's location relative to the
|
||||
* {@code rootPath}.
|
||||
*
|
||||
* @param filePath The path to the JSON file.
|
||||
* @param type The registry type category (e.g., "items", "quests").
|
||||
* @return A {@link Key} representing the namespace and internal path.
|
||||
*/
|
||||
public Key getFileKey(Path filePath, String type) {
|
||||
var relative = rootPath.relativize(filePath);
|
||||
var namespace = relative.getName(0).toString();
|
||||
|
||||
var valuePath = relative.subpath(2, relative.getNameCount()).toString()
|
||||
.replace('\\', '/')
|
||||
.replace(".json", "");
|
||||
|
||||
return Key.key(namespace, valuePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines a {@link Path} based on a provided key relative to the
|
||||
* {@code rootPath}.
|
||||
*
|
||||
* @param key The key identifier.
|
||||
* @param type The registry type category (e.g., "items", "quests").
|
||||
* @return A {@link Path} representing the key.
|
||||
*/
|
||||
public Path getKeyFile(Key key, String type) {
|
||||
return rootPath.resolve(key.namespace())
|
||||
.resolve(type)
|
||||
.resolve(key.value() + ".json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all objects of a specific type category.
|
||||
*
|
||||
* @param type The registry type category (e.g., "items", "quests").
|
||||
* @param classType The class to deserialize into.
|
||||
* @param <T> A type extending {@link Keyed}.
|
||||
* @return A collection of loaded objects.
|
||||
*/
|
||||
public <T extends Keyed> Collection<T> loadAll(String type, Class<T> classType) {
|
||||
if (!Files.exists(rootPath))
|
||||
return Collections.emptyList();
|
||||
|
||||
List<T> loaded = new ArrayList<>();
|
||||
try (var namespaces = Files.list(rootPath)) {
|
||||
for (var nsPath : namespaces.filter(Files::isDirectory).toList()) {
|
||||
var typePath = nsPath.resolve(type);
|
||||
if (!Files.exists(typePath))
|
||||
continue;
|
||||
|
||||
try (var files = Files.walk(typePath)) {
|
||||
var results = files
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(path -> path.toString().endsWith(".json"))
|
||||
.map(path -> loadFile(path, type, classType))
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
loaded.addAll(results);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Error while walking through registry: {}", e.getMessage());
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all objects of a specific type category and add them into provided
|
||||
* registry.
|
||||
*
|
||||
* @param registry The registry to add loaded object into.
|
||||
* @param <T> A type extending {@link Keyed}.
|
||||
*/
|
||||
public <T extends Keyed> void loadAll(Registry<T> registry) {
|
||||
registry.addAll(loadAll(registry.type(), registry.typeClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a collection of objects to the filesystem.
|
||||
*
|
||||
* @param values The values collection to save.
|
||||
* @param type The registry type category (e.g., "items", "quests").
|
||||
* @param <T> A type extending {@link Keyed}.
|
||||
*/
|
||||
public <T extends Keyed> void saveAll(Collection<T> values, String type) {
|
||||
values.forEach(x -> saveFile(x, type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all objects from a registry, invoking
|
||||
* {@link Registry#onBeforeSave(Object)}
|
||||
* on each item before writing to disk.
|
||||
*
|
||||
* @param registry The registry to save.
|
||||
* @param <T> A type extending {@link Keyed}.
|
||||
*/
|
||||
public <T extends Keyed> void saveAll(Registry<T> registry) {
|
||||
registry.getAll().stream()
|
||||
.map(registry::onBeforeSave)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(x -> saveFile(x, registry.type()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user