diff --git a/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java b/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java
index 8e8c1fa..01162c6 100644
--- a/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java
+++ b/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java
@@ -43,6 +43,7 @@ import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
@@ -60,6 +61,8 @@ import org.jetbrains.annotations.NotNull;
*
* This class is not meant to be used outside Patchwork.
*/
+@ApiStatus.Internal
+@ApiStatus.NonExtendable
public final class BukkitDelegate extends Command implements PluginIdentifiableCommand
{
private final JavaPlugin plugin;
@@ -274,3 +277,4 @@ public final class BukkitDelegate extends Command implements PluginIdentifiableC
return this.plugin;
}
}
+
diff --git a/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java b/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java
index 54f2a30..20be942 100644
--- a/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java
+++ b/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java
@@ -27,6 +27,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.jetbrains.annotations.ApiStatus;
/**
* A marker interface which represents a holder for multiple {@link Completion} annotations.
@@ -36,6 +37,7 @@ import java.lang.annotation.Target;
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
+@ApiStatus.Internal
public @interface Completions
{
/**
diff --git a/Patchwork/src/main/java/fns/patchwork/config/ConfigType.java b/Patchwork/src/main/java/fns/patchwork/config/ConfigType.java
new file mode 100644
index 0000000..1a77e45
--- /dev/null
+++ b/Patchwork/src/main/java/fns/patchwork/config/ConfigType.java
@@ -0,0 +1,88 @@
+/*
+ * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite
+ * Copyright (C) 2023 Simplex Development and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package fns.patchwork.config;
+
+import com.electronwill.nightconfig.core.Config;
+import com.electronwill.nightconfig.core.ConfigFormat;
+import com.electronwill.nightconfig.core.io.ConfigParser;
+import com.electronwill.nightconfig.core.io.ConfigWriter;
+import com.electronwill.nightconfig.json.FancyJsonWriter;
+import com.electronwill.nightconfig.json.JsonFormat;
+import com.electronwill.nightconfig.json.JsonParser;
+import com.electronwill.nightconfig.json.MinimalJsonWriter;
+import com.electronwill.nightconfig.toml.TomlFormat;
+import com.electronwill.nightconfig.toml.TomlParser;
+import com.electronwill.nightconfig.toml.TomlWriter;
+
+public enum ConfigType
+{
+ TOML(TomlFormat.instance(),
+ ".toml",
+ new TomlWriter(),
+ new TomlParser()),
+ JSON(JsonFormat.minimalInstance(),
+ ".json",
+ new MinimalJsonWriter(),
+ new JsonParser()),
+ JSON_FANCY(JsonFormat.fancyInstance(),
+ ".json",
+ new FancyJsonWriter(),
+ new JsonParser());
+
+ private final ConfigFormat> format;
+ private final String fileExtension;
+ private final ConfigWriter writer;
+ private final ConfigParser> parser;
+
+ ConfigType(final ConfigFormat> format,
+ final String fileExtension,
+ final ConfigWriter writer,
+ final ConfigParser> parser)
+ {
+ this.format = format;
+ this.fileExtension = fileExtension;
+ this.writer = writer;
+ this.parser = parser;
+ }
+
+ public ConfigFormat> getFormat()
+ {
+ return this.format;
+ }
+
+ public String getExtension()
+ {
+ return this.fileExtension;
+ }
+
+ public ConfigWriter getWriter()
+ {
+ return this.writer;
+ }
+
+ public ConfigParser> getParser()
+ {
+ return this.parser;
+ }
+}
diff --git a/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java b/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java
new file mode 100644
index 0000000..410e791
--- /dev/null
+++ b/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java
@@ -0,0 +1,224 @@
+/*
+ * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite
+ * Copyright (C) 2023 Simplex Development and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package fns.patchwork.config;
+
+import com.electronwill.nightconfig.core.Config;
+import com.electronwill.nightconfig.core.ConfigFormat;
+import com.electronwill.nightconfig.core.UnmodifiableConfig;
+import fns.patchwork.utils.FileUtils;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.Unmodifiable;
+
+public final class GenericConfiguration implements Configuration
+{
+ private final File configFile;
+ private final String fileName;
+ private final Config config;
+ private final ConfigType configType;
+
+ public GenericConfiguration(@NotNull final ConfigType configType,
+ @Nullable final JavaPlugin plugin,
+ @NotNull final File dataFolder,
+ @NotNull final String fileName,
+ final boolean isConcurrent) throws IOException
+ {
+ if (!fileName.endsWith(configType.getExtension()))
+ throw new IllegalArgumentException("File name must end with " + configType.getExtension() + "!");
+
+ // Ternary just to piss off Allink :)
+ final Optional file = (plugin != null) ?
+ FileUtils.getOrCreateFileWithResource(dataFolder, fileName, plugin) :
+ FileUtils.getOrCreateFile(dataFolder, fileName);
+
+ if (file.isEmpty())
+ throw new FileNotFoundException();
+
+ this.configFile = file.get();
+ this.fileName = fileName;
+ this.configType = configType;
+
+ final ConfigFormat> format = configType.getFormat();
+
+ // Another ternary just to piss off Allink :)
+ this.config = isConcurrent ? format.createConcurrentConfig() : format.createConfig();
+
+ this.load();
+ }
+
+ public GenericConfiguration(final ConfigType type, final File dataFolder, final String fileName)
+ throws IOException
+ {
+ this(type, null, dataFolder, fileName, false);
+ }
+
+ public GenericConfiguration(final ConfigType type, final JavaPlugin plugin, final String fileName)
+ throws IOException
+ {
+ this(type, plugin, plugin.getDataFolder(), fileName, false);
+ }
+
+ public GenericConfiguration(final ConfigType type, final File dataFolder, final String fileName,
+ final boolean isConcurrent)
+ throws IOException
+ {
+ this(type, null, dataFolder, fileName, isConcurrent);
+ }
+
+ @Override
+ public void save() throws IOException
+ {
+ final File backup = new File(this.configFile.getParentFile(), this.fileName + ".bak");
+
+ if (backup.exists())
+ Files.delete(backup.toPath());
+
+ Files.copy(this.configFile.toPath(), backup.toPath());
+
+ try (final FileWriter writer = new FileWriter(this.configFile))
+ {
+ this.configType.getWriter().write(this.getConfig(), writer);
+ }
+ }
+
+ @Override
+ public void load() throws IOException {
+ try (final FileReader reader = new FileReader(this.configFile)) {
+ this.config.clear();
+
+ final UnmodifiableConfig parsed = this.configType.getParser().parse(reader).unmodifiable();
+ this.config.putAll(parsed);
+ }
+ }
+
+ @Override
+ public String getFileName()
+ {
+ return fileName;
+ }
+
+ @Override
+ public File getConfigurationFile()
+ {
+ return configFile;
+ }
+
+ @Override
+ public String getString(final String path)
+ {
+ if (!(this.getConfig().get(path) instanceof String))
+ throw new IllegalArgumentException(String.format("Value at path %s is not a string!", path));
+
+ return this.getConfig().get(path);
+ }
+
+ @Override
+ public boolean getBoolean(String path)
+ {
+ if (!(this.getConfig().get(path) instanceof Boolean))
+ throw new IllegalArgumentException(String.format("Value at path %s is not a boolean!", path));
+
+ return this.getConfig().get(path);
+ }
+
+ @Override
+ @ApiStatus.Internal
+ public @Unmodifiable List getList(String path, Class clazz)
+ {
+ // TODO: Figure out how to parse lists with Night Config.
+
+ return new ArrayList<>();
+ }
+
+ @Override
+ @ApiStatus.Internal
+ public @Unmodifiable List getStringList(String path)
+ {
+ // TODO: Figure out how to parse lists with Night Config.
+
+ return new ArrayList<>();
+ }
+
+ @Override
+ public int getInt(String path)
+ {
+ return this.getConfig().getInt(path);
+ }
+
+ @Override
+ public long getLong(String path)
+ {
+ return this.getConfig().getLong(path);
+ }
+
+ @Override
+ public double getDouble(String path)
+ {
+ if (!(this.getConfig().get(path) instanceof Double))
+ throw new IllegalArgumentException(String.format("Value at path %s is not a double!", path));
+
+ return this.getConfig().get(path);
+ }
+
+ @Override
+ public Optional get(String path, Class clazz)
+ {
+ // I love ternary statements, sorry Allink :)
+ return clazz.isInstance(this.getConfig().get(path)) ?
+ Optional.of(clazz.cast(this.getConfig().get(path))) :
+ Optional.empty();
+ }
+
+ @Override
+ public T getOrDefault(String path, Class clazz, T fallback)
+ {
+ return this.get(path, clazz).orElse(fallback);
+ }
+
+ @Override
+ public void set(final String path, final T value) {
+ this.config.set(path, value);
+ }
+
+ private UnmodifiableConfig getConfig()
+ {
+ return config.unmodifiable();
+ }
+
+ public ConfigType getConfigType()
+ {
+ return configType;
+ }
+}
diff --git a/Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java b/Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java
deleted file mode 100644
index 585ed4e..0000000
--- a/Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite
- * Copyright (C) 2023 Total Freedom Server Network and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package fns.patchwork.config;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import org.bukkit.plugin.java.JavaPlugin;
-import org.jetbrains.annotations.Unmodifiable;
-import org.tomlj.Toml;
-import org.tomlj.TomlParseResult;
-
-// TODO: Finish implementation
-public class WrappedTomlConfiguration implements Configuration
-{
- private final Map previousValues = new HashMap<>();
- private final TomlParseResult toml;
- private final File file;
-
-
- public WrappedTomlConfiguration(final JavaPlugin plugin, final File file) throws IOException
- {
- if (!file.exists() && file.createNewFile())
- {
- plugin.saveResource(file.getName(), true);
- }
-
- this.toml = Toml.parse(Path.of(file.toURI()));
- this.file = file;
- }
-
- @Override
- public void save() throws IOException
- {
- // Create a backup file
- final File backup = new File(this.file.getParentFile(), this.file.getName() + ".bak");
- if (backup.exists() && !Files.deleteIfExists(Path.of(backup.toURI())))
- {
- throw new IOException("Failed to delete existing backup file: " + backup.getName());
- }
-
- // Serialize the current configuration to a temporary file
- final File tempFile = new File(this.file.getParentFile(), this.file.getName() + ".temp");
- try (FileWriter tempFileWriter = new FileWriter(tempFile))
- {
- // Convert the updated TomlTable to TOML format and write it to the temporary file
- String tomlString = this.toml.toToml();
- tempFileWriter.write(tomlString);
- }
-
- // Compare the new configuration with the previous one
- TomlParseResult newToml = Toml.parse(Path.of(tempFile.toURI()));
- for (Map.Entry entry : newToml.toMap().entrySet())
- {
- String key = entry.getKey();
- Object newValue = entry.getValue();
- Object oldValue = previousValues.get(key);
-
- if (oldValue == null || !oldValue.equals(newValue))
- {
- // Value has changed, update it
- this.toml.toMap().replace(key, newValue);
- previousValues.put(key, newValue);
- }
- }
-
- // Save the updated configuration to the original file
- try (FileWriter fileWriter = new FileWriter(this.file))
- {
- // Convert the updated TomlTable to TOML format and write it to the original file
- String tomlString = this.toml.toToml();
- fileWriter.write(tomlString);
- }
-
- // Delete the temporary file and the backup file
- Files.delete(Path.of(tempFile.toURI()));
- Files.delete(Path.of(backup.toURI()));
- }
-
- @Override
- public void load() throws IOException
- {
- // TODO: Implement
- }
-
- @Override
- public String getFileName()
- {
- return null;
- }
-
- @Override
- public File getConfigurationFile()
- {
- return null;
- }
-
- @Override
- public String getString(String path)
- {
- return null;
- }
-
- @Override
- public boolean getBoolean(String path)
- {
- return false;
- }
-
- @Override
- public @Unmodifiable List getList(String path, Class clazz)
- {
- return null;
- }
-
- @Override
- public @Unmodifiable List getStringList(String path)
- {
- return null;
- }
-
- @Override
- public int getInt(String path)
- {
- return 0;
- }
-
- @Override
- public long getLong(String path)
- {
- return 0;
- }
-
- @Override
- public double getDouble(String path)
- {
- return 0;
- }
-
- @Override
- public void set(String path, T value)
- {
- // TODO: Implement
- }
-
- @Override
- public Optional get(String path, Class clazz)
- {
- return Optional.empty();
- }
-
- @Override
- public T getOrDefault(String path, Class clazz, T fallback)
- {
- return null;
- }
-}
diff --git a/Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java b/Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java
new file mode 100644
index 0000000..0f62b70
--- /dev/null
+++ b/Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java
@@ -0,0 +1,129 @@
+/*
+ * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite
+ * Copyright (C) 2023 Simplex Development and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package fns.patchwork.utils;
+
+import fns.patchwork.utils.logging.FNS4J;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+public final class FileUtils
+{
+ @NonNls
+ private static final String CREATED_DIRECTORY = "Created directory ";
+
+ private FileUtils()
+ {
+ throw new AssertionError();
+ }
+
+ public static String getExtension(@NotNull final File file)
+ {
+ return file.getName()
+ .substring(file.getName()
+ .lastIndexOf('.'));
+ }
+
+ public static Optional getOrCreateDirectory(final File parentDirectory, final String directoryName)
+ {
+ if (parentDirectory.mkdirs())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + parentDirectory.getAbsolutePath());
+
+ final File directory = new File(parentDirectory, directoryName);
+ if (directory.mkdirs())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + directory.getAbsolutePath());
+
+ if (directory.exists())
+ return Optional.of(directory);
+
+ return Optional.empty();
+ }
+
+ public static Optional getOrCreateDirectory(final Path directoryPath)
+ {
+ Optional directory = Optional.empty();
+
+ if (directoryPath.toFile().mkdirs())
+ directory = Optional.of(directoryPath.toFile());
+
+ if (directory.isPresent())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + directoryPath.toAbsolutePath());
+
+ return directory;
+ }
+
+ public static Optional getOrCreateFile(final File parentDirectory, final String fileName)
+ {
+ if (parentDirectory.mkdirs())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + parentDirectory.getAbsolutePath());
+
+ final File file = new File(parentDirectory, fileName);
+ try
+ {
+ if (file.createNewFile())
+ FNS4J.PATCHWORK.info("Created file " + file.getAbsolutePath());
+
+ return Optional.of(file);
+ }
+ catch (final IOException ex)
+ {
+ FNS4J.PATCHWORK.error("Failed to create file " + fileName + ": " + ex.getMessage());
+ return Optional.empty();
+ }
+ }
+
+ public static Optional getOrCreateFileWithResource(final File parentDirectory,
+ final String fileName,
+ final JavaPlugin plugin)
+ {
+ if (parentDirectory.mkdirs())
+ FNS4J.PATCHWORK.info(CREATED_DIRECTORY + parentDirectory.getAbsolutePath());
+
+ final File file = new File(parentDirectory, fileName);
+ try
+ {
+ if (file.createNewFile())
+ {
+ FNS4J.PATCHWORK.info("Created file " + file.getAbsolutePath());
+ FNS4J.PATCHWORK.info(
+ "Copying default file from resources/" + fileName + " to " + file.getAbsolutePath());
+
+ plugin.saveResource(fileName, true);
+ FNS4J.PATCHWORK.info(
+ "Successfully copied default file from resources/" + fileName + " to " + file.getAbsolutePath());
+ }
+
+ return Optional.of(file);
+ }
+ catch (final IOException ex)
+ {
+ FNS4J.PATCHWORK.error("Failed to create file " + fileName + ": " + ex.getMessage());
+ return Optional.empty();
+ }
+ }
+}