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; 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.Schematic;
import me.youhavetrouble.blockedit.schematic.SchematicProvider; import me.youhavetrouble.blockedit.schematic.SchematicProvider;
import me.youhavetrouble.blockedit.util.Clipboard; import me.youhavetrouble.blockedit.util.Clipboard;
import me.youhavetrouble.blockedit.util.NameFilenameFilter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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>> schematicProvidersByExtension = new HashMap<>();
private final Map<String, SchematicProvider<S>> schematicProvidersByName = new HashMap<>(); private final Map<String, SchematicProvider<S>> schematicProvidersByName = new HashMap<>();
private final File schematicsDirectory; public final File schematicsDirectory;
protected SchematicHandler(BlockEdit plugin) { protected SchematicHandler(BlockEdit plugin) {
this.plugin = plugin; this.plugin = plugin;
@@ -35,40 +40,53 @@ public class SchematicHandler<S extends Schematic> {
public void registerSchematicProvider( public void registerSchematicProvider(
@NotNull SchematicProvider<S> schematicProvider @NotNull SchematicProvider<S> schematicProvider
) throws IllegalArgumentException { ) throws SchematicHandlerRegistrationException {
if (!schematicProvider.name().matches("^[a-z0-9]+$")) {
throw new IllegalArgumentException("Schematic provider name can only contain lowercase letters and numbers"); 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())) { if (schematicProvidersByName.containsKey(schematicProviderName)) {
throw new IllegalArgumentException("Schematic provider " + schematicProvider.name() + " is already registered"); 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 // Register the provider as a file provider that associates the provider with file extensions
for (String extension : schematicProvider.fileExtensions()) { if (schematicProvider instanceof FileSchematicProvider<S> fileSchematicProvider) {
String trimmedExtension = extension.trim(); // Loop extensions to verify if they're valid and not already registered
if (trimmedExtension.isEmpty()) throw new IllegalArgumentException("File extension cannot be empty"); for (String extension : fileSchematicProvider.fileExtensions()) {
if (!trimmedExtension.matches("^[a-z0-9]+$")) throw new IllegalArgumentException("File extension can only contain lowercase letters and numbers"); String trimmedExtension = extension.trim();
if (schematicProvidersByExtension.containsKey(trimmedExtension)) { if (trimmedExtension.isEmpty()) throw new SchematicHandlerRegistrationException("File extension cannot be empty", fileSchematicProvider);
throw new IllegalArgumentException("File extension " + trimmedExtension + " is already registered to " + schematicProvidersByExtension.get(trimmedExtension).name()); 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); 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 providerName Schematic provider to use
* @param schematicName Name of the schematic * @param schematicName Name of the schematic
* @return Clipboard object containing the schematic. Null if schematic does not exist or could not be loaded. * @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); SchematicProvider<? extends Schematic> schematicProvider = schematicProvidersByName.get(providerName);
if (schematicProvider == null) { if (schematicProvider == null) {
throw new IllegalArgumentException("Schematic provider " + providerName + " is not registered"); throw new IllegalArgumentException("Schematic provider " + providerName + " is not registered");
@@ -76,6 +94,34 @@ public class SchematicHandler<S extends Schematic> {
return schematicProvider.load(schematicName); 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 * Saves a schematic to the schematics directory
* @param schematicName Name of the schematic * @param schematicName Name of the schematic
@@ -85,15 +131,13 @@ public class SchematicHandler<S extends Schematic> {
@NotNull String schematicName, @NotNull String schematicName,
@NotNull String providerName, @NotNull String providerName,
@NotNull Clipboard clipboard @NotNull Clipboard clipboard
) { ) throws SchematicSaveException {
SchematicProvider<S> schematicProvider = schematicProvidersByName.get(providerName); SchematicProvider<S> schematicProvider = schematicProvidersByName.get(providerName);
if (schematicProvider == null) { 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); S schematic = schematicProvider.fromClipboard(schematicName, clipboard);
schematicProvider.save(schematic); 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; package me.youhavetrouble.blockedit.exception;
public class SchematicLoadException extends RuntimeException { import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public SchematicLoadException(String message) { public class SchematicLoadException extends SchematicException {
super(message);
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(); @NotNull String name();
/**
* Get the file extensions of the schematic provider.
*/
@NotNull String[] fileExtensions();
/** /**
* Save the schematic * 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);
}
}