expanded the schematic api draft

This commit is contained in:
2025-05-19 20:52:02 +02:00
parent 015f1abf49
commit 7383c74fcc
8 changed files with 179 additions and 31 deletions
@@ -1,8 +1,13 @@
package me.youhavetrouble.blockedit;
import me.youhavetrouble.blockedit.exception.SchematicHandlerRegistrationException;
import me.youhavetrouble.blockedit.exception.SchematicLoadException;
import me.youhavetrouble.blockedit.exception.SchematicSaveException;
import me.youhavetrouble.blockedit.schematic.FileSchematicProvider;
import me.youhavetrouble.blockedit.schematic.Schematic;
import me.youhavetrouble.blockedit.schematic.SchematicProvider;
import me.youhavetrouble.blockedit.util.Clipboard;
import me.youhavetrouble.blockedit.util.NameFilenameFilter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -17,7 +22,7 @@ public class SchematicHandler<S extends Schematic> {
private final Map<String, SchematicProvider<S>> schematicProvidersByExtension = new HashMap<>();
private final Map<String, SchematicProvider<S>> schematicProvidersByName = new HashMap<>();
private final File schematicsDirectory;
public final File schematicsDirectory;
protected SchematicHandler(BlockEdit plugin) {
this.plugin = plugin;
@@ -35,40 +40,53 @@ public class SchematicHandler<S extends Schematic> {
public void registerSchematicProvider(
@NotNull SchematicProvider<S> schematicProvider
) throws IllegalArgumentException {
if (!schematicProvider.name().matches("^[a-z0-9]+$")) {
throw new IllegalArgumentException("Schematic provider name can only contain lowercase letters and numbers");
) throws SchematicHandlerRegistrationException {
String schematicProviderName = schematicProvider.name().trim();
if (!schematicProviderName.matches("^[a-z0-9]+$")) {
throw new SchematicHandlerRegistrationException("Schematic provider name can only contain lowercase letters and numbers", schematicProvider);
}
if (schematicProvidersByName.containsKey(schematicProvider.name())) {
throw new IllegalArgumentException("Schematic provider " + schematicProvider.name() + " is already registered");
if (schematicProvidersByName.containsKey(schematicProviderName)) {
throw new SchematicHandlerRegistrationException("Schematic provider with name " + schematicProvider.name() + " is already registered", schematicProvider);
}
// Loop extensions to verify if they're valid and not already registered
for (String extension : schematicProvider.fileExtensions()) {
String trimmedExtension = extension.trim();
if (trimmedExtension.isEmpty()) throw new IllegalArgumentException("File extension cannot be empty");
if (!trimmedExtension.matches("^[a-z0-9]+$")) throw new IllegalArgumentException("File extension can only contain lowercase letters and numbers");
if (schematicProvidersByExtension.containsKey(trimmedExtension)) {
throw new IllegalArgumentException("File extension " + trimmedExtension + " is already registered to " + schematicProvidersByExtension.get(trimmedExtension).name());
// Register the provider as a file provider that associates the provider with file extensions
if (schematicProvider instanceof FileSchematicProvider<S> fileSchematicProvider) {
// Loop extensions to verify if they're valid and not already registered
for (String extension : fileSchematicProvider.fileExtensions()) {
String trimmedExtension = extension.trim();
if (trimmedExtension.isEmpty()) throw new SchematicHandlerRegistrationException("File extension cannot be empty", fileSchematicProvider);
if (!trimmedExtension.matches("^[a-z0-9]+$")) throw new SchematicHandlerRegistrationException("File extension can only contain lowercase letters and numbers", fileSchematicProvider);
if (schematicProvidersByExtension.containsKey(trimmedExtension)) {
throw new SchematicHandlerRegistrationException("File extension " + trimmedExtension + " is already registered to " + schematicProvidersByExtension.get(trimmedExtension).name(), fileSchematicProvider);
}
}
// Loop again to actually register the extensions
for (String extension : fileSchematicProvider.fileExtensions()) {
String trimmedExtension = extension.trim();
schematicProvidersByExtension.put(trimmedExtension, schematicProvider);
}
return;
}
// Loop again to actually register the extensions
for (String extension : schematicProvider.fileExtensions()) {
String trimmedExtension = extension.trim();
schematicProvidersByExtension.put(trimmedExtension, schematicProvider);
}
schematicProvidersByName.put(schematicProvider.name(), schematicProvider);
}
/**
* Loads a schematic from the schematics directory
* Loads a schematic from the schematic provider
* @param providerName Schematic provider to use
* @param schematicName Name of the schematic
* @return Clipboard object containing the schematic. Null if schematic does not exist or could not be loaded.
*/
public @Nullable Schematic loadSchematic(String providerName, @NotNull String schematicName) {
public @Nullable Schematic loadSchematic(@NotNull String providerName, @NotNull String schematicName) {
// file provider is a special case here
if (providerName.equals("file")) {
return loadSchematic(schematicName);
}
SchematicProvider<? extends Schematic> schematicProvider = schematicProvidersByName.get(providerName);
if (schematicProvider == null) {
throw new IllegalArgumentException("Schematic provider " + providerName + " is not registered");
@@ -76,6 +94,34 @@ public class SchematicHandler<S extends Schematic> {
return schematicProvider.load(schematicName);
}
/**
* Loads a schematic from the schematic directory. File schematic provider will be matched by the file extenstion
* @param schematicName File name without extension
* @return Schematic object
*/
public @Nullable Schematic loadSchematic(@NotNull String schematicName) throws SchematicLoadException {
File[] files;
try {
files = this.schematicsDirectory.listFiles(new NameFilenameFilter(schematicName));
if (files == null || files.length == 0) return null;
} catch (SecurityException e) {
throw new SchematicLoadException("Could not list schematics directory", schematicName, e);
}
File schematicFile = files[0];
String fileName = schematicFile.getName();
String fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1);
SchematicProvider<S> schematicProvider = schematicProvidersByExtension.get(fileExtension);
if (schematicProvider == null) {
throw new SchematicLoadException("No schematic provider found for file extension " + fileExtension, schematicName);
}
Schematic schematic = schematicProvider.load(schematicName);
if (schematic == null) {
throw new SchematicLoadException("Schematic could not be loaded", schematicName);
}
return schematic;
}
/**
* Saves a schematic to the schematics directory
* @param schematicName Name of the schematic
@@ -85,15 +131,13 @@ public class SchematicHandler<S extends Schematic> {
@NotNull String schematicName,
@NotNull String providerName,
@NotNull Clipboard clipboard
) {
) throws SchematicSaveException {
SchematicProvider<S> schematicProvider = schematicProvidersByName.get(providerName);
if (schematicProvider == null) {
throw new IllegalArgumentException("Schematic provider " + providerName + " is not registered");
throw new SchematicSaveException("Schematic provider " + providerName + " is not registered", schematicName);
}
S schematic = schematicProvider.fromClipboard(schematicName, clipboard);
schematicProvider.save(schematic);
}
}
@@ -0,0 +1,25 @@
package me.youhavetrouble.blockedit.exception;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SchematicException extends RuntimeException {
private final String schematicName;
public SchematicException(@NotNull String message, @Nullable String schematicName, Throwable cause) {
super(message);
this.schematicName = schematicName;
initCause(cause);
}
public SchematicException(@NotNull String message, @Nullable String schematicName) {
super(message);
this.schematicName = schematicName;
}
public String getSchematicName() {
return schematicName;
}
}
@@ -0,0 +1,24 @@
package me.youhavetrouble.blockedit.exception;
import me.youhavetrouble.blockedit.schematic.SchematicProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SchematicHandlerRegistrationException extends IllegalArgumentException {
private final SchematicProvider<?> schematicProvider;
public SchematicHandlerRegistrationException(
@NotNull String message,
@Nullable SchematicProvider<?> schematicProvider
) {
super(message);
this.schematicProvider = schematicProvider;
}
@Nullable
public SchematicProvider<?> getSchematicProvider() {
return schematicProvider;
}
}
@@ -1,9 +1,16 @@
package me.youhavetrouble.blockedit.exception;
public class SchematicLoadException extends RuntimeException {
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public SchematicLoadException(String message) {
super(message);
public class SchematicLoadException extends SchematicException {
public SchematicLoadException(@NotNull String message, @NotNull String schematicName, @Nullable Throwable cause) {
super(message, schematicName, cause);
}
public SchematicLoadException(@NotNull String message, @NotNull String schematicName) {
super(message, schematicName);
}
}
@@ -0,0 +1,14 @@
package me.youhavetrouble.blockedit.exception;
import org.jetbrains.annotations.NotNull;
public class SchematicSaveException extends SchematicException {
public SchematicSaveException(@NotNull String message, @NotNull String schematicName, Throwable cause) {
super(message, schematicName, cause);
}
public SchematicSaveException(@NotNull String message, @NotNull String schematicName) {
super(message, schematicName);
}
}
@@ -0,0 +1,12 @@
package me.youhavetrouble.blockedit.schematic;
import org.jetbrains.annotations.NotNull;
public interface FileSchematicProvider<S extends Schematic> extends SchematicProvider<S> {
/**
* Get the file extensions of the schematic provider.
*/
@NotNull String[] fileExtensions();
}
@@ -15,10 +15,7 @@ public interface SchematicProvider<S extends Schematic> {
*/
@NotNull String name();
/**
* Get the file extensions of the schematic provider.
*/
@NotNull String[] fileExtensions();
/**
* Save the schematic
@@ -0,0 +1,25 @@
package me.youhavetrouble.blockedit.util;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FilenameFilter;
import java.util.List;
public class NameFilenameFilter implements FilenameFilter {
private final String filterName;
public NameFilenameFilter(@NotNull String name) {
this.filterName = name;
}
@Override
public boolean accept(File dir, String name) {
String[] parts = name.split("\\.");
if (parts.length < 2) return filterName.equals(name);
String joined = String.join(".", List.of(parts).subList(0, parts.length - 1));
return joined.equals(filterName);
}
}