mirror of
https://github.com/Multiverse/Multiverse-Core.git
synced 2024-11-15 07:05:28 +01:00
Merge pull request #2900 from Multiverse/better-revamp-config
refactor: Abstract handle class and improve node handling
This commit is contained in:
commit
aac01b9e7e
@ -2,6 +2,7 @@ package com.onarandombox.MultiverseCore.api;
|
||||
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
|
||||
import com.onarandombox.MultiverseCore.placeholders.MultiverseCorePlaceholders;
|
||||
import io.vavr.control.Try;
|
||||
import org.jvnet.hk2.annotations.Contract;
|
||||
|
||||
@Contract
|
||||
@ -34,19 +35,21 @@ public interface MVConfig {
|
||||
/**
|
||||
* Gets a property from the config.
|
||||
*
|
||||
* @param name The name of the property.
|
||||
* @return The value of the property.
|
||||
* @param name The name of the property.
|
||||
* @return A {@link Try} with the value of the property, otherwise a {@link Try.Failure} if there is no property by
|
||||
* that name.
|
||||
*/
|
||||
Object getProperty(String name);
|
||||
Try<Object> getProperty(String name);
|
||||
|
||||
/**
|
||||
* Sets a property in the config.
|
||||
*
|
||||
* @param name The name of the property.
|
||||
* @param value The value of the property.
|
||||
* @return True if the property was set successfully.
|
||||
* @return An empty {@link Try} if the property was set successfully, otherwise a {@link Try.Failure} with the
|
||||
* exception explaining why the property could not be set.
|
||||
*/
|
||||
boolean setProperty(String name, Object value);
|
||||
Try<Void> setProperty(String name, Object value);
|
||||
|
||||
/**
|
||||
* Sets world access permissions should be enforced.
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.onarandombox.MultiverseCore.commands;
|
||||
|
||||
import co.aikar.commands.BukkitCommandIssuer;
|
||||
import co.aikar.commands.annotation.CommandAlias;
|
||||
import co.aikar.commands.annotation.CommandCompletion;
|
||||
import co.aikar.commands.annotation.CommandPermission;
|
||||
@ -9,11 +8,12 @@ import co.aikar.commands.annotation.Optional;
|
||||
import co.aikar.commands.annotation.Single;
|
||||
import co.aikar.commands.annotation.Subcommand;
|
||||
import co.aikar.commands.annotation.Syntax;
|
||||
import com.onarandombox.MultiverseCore.api.MVConfig;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
|
||||
import com.onarandombox.MultiverseCore.commandtools.context.MVConfigValue;
|
||||
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
|
||||
import com.onarandombox.MultiverseCore.exceptions.MultiverseException;
|
||||
import jakarta.inject.Inject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
@ -35,7 +35,7 @@ public class ConfigCommand extends MultiverseCommand {
|
||||
@CommandCompletion("@mvconfigs")
|
||||
@Syntax("<name> [new-value]")
|
||||
@Description("") //TODO
|
||||
public void onConfigCommand(BukkitCommandIssuer issuer,
|
||||
public void onConfigCommand(MVCommandIssuer issuer,
|
||||
|
||||
@Syntax("<name>")
|
||||
@Description("") //TODO
|
||||
@ -54,21 +54,26 @@ public class ConfigCommand extends MultiverseCommand {
|
||||
updateConfigValue(issuer, name, value.getValue());
|
||||
}
|
||||
|
||||
private void showConfigValue(BukkitCommandIssuer issuer, String name) {
|
||||
Object currentValue = config.getProperty(name);
|
||||
if (currentValue == null) {
|
||||
issuer.sendMessage("No such config option: " + name);
|
||||
return;
|
||||
}
|
||||
issuer.sendMessage(name + "is currently set to " + config.getProperty(name));
|
||||
private void showConfigValue(MVCommandIssuer issuer, String name) {
|
||||
config.getProperty(name)
|
||||
.onSuccess(value -> issuer.sendMessage(name + "is currently set to " + value))
|
||||
.onFailure(e -> issuer.sendMessage("Unable to get " + name + ": " + e.getMessage()));
|
||||
}
|
||||
|
||||
private void updateConfigValue(BukkitCommandIssuer issuer, String name, Object value) {
|
||||
if (!config.setProperty(name, value)) {
|
||||
issuer.sendMessage("Unable to set " + name + " to " + value);
|
||||
return;
|
||||
}
|
||||
config.save();
|
||||
issuer.sendMessage("Successfully set " + name + " to " + value);
|
||||
private void updateConfigValue(MVCommandIssuer issuer, String name, Object value) {
|
||||
config.setProperty(name, value)
|
||||
.onSuccess(ignore -> {
|
||||
config.save();
|
||||
issuer.sendMessage("Successfully set " + name + " to " + value);
|
||||
})
|
||||
.onFailure(e -> {
|
||||
issuer.sendMessage("Unable to set " + name + " to " + value + ".");
|
||||
if (e instanceof MultiverseException) {
|
||||
var message = ((MultiverseException) e).getMVMessage();
|
||||
if (message != null) {
|
||||
issuer.sendError(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -15,14 +15,15 @@ import com.onarandombox.MultiverseCore.api.MVWorldManager;
|
||||
import com.onarandombox.MultiverseCore.commandtools.context.GameRuleValue;
|
||||
import com.onarandombox.MultiverseCore.commandtools.context.MVConfigValue;
|
||||
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.Node;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
|
||||
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
|
||||
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
|
||||
import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
|
||||
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
|
||||
import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
|
||||
import com.onarandombox.MultiverseCore.utils.PlayerFinder;
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.CommentedNode;
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.TypedValueNode;
|
||||
import io.vavr.control.Option;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.GameRule;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -123,7 +124,7 @@ public class MVCommandContexts extends PaperCommandContexts {
|
||||
if (Strings.isNullOrEmpty(configName)) {
|
||||
throw new InvalidCommandArgument("No config name specified.");
|
||||
}
|
||||
Optional<CommentedNode> node = config.getNodes().findNode(configName);
|
||||
Option<Node> node = config.getNodes().findNode(configName);
|
||||
if (node.isEmpty()) {
|
||||
throw new InvalidCommandArgument("The config " + configName + " is not valid.");
|
||||
}
|
||||
@ -133,12 +134,12 @@ public class MVCommandContexts extends PaperCommandContexts {
|
||||
throw new InvalidCommandArgument("No config value specified.");
|
||||
}
|
||||
|
||||
if (!(node.get() instanceof TypedValueNode)) {
|
||||
if (!(node.get() instanceof ValueNode)) {
|
||||
context.popFirstArg();
|
||||
return new MVConfigValue(valueString);
|
||||
}
|
||||
|
||||
ContextResolver<?, BukkitCommandExecutionContext> resolver = getResolver(((TypedValueNode<?>) node.get()).getType());
|
||||
ContextResolver<?, BukkitCommandExecutionContext> resolver = getResolver(((ValueNode<?>) node.get()).getType());
|
||||
if (resolver == null) {
|
||||
context.popFirstArg();
|
||||
return new MVConfigValue(valueString);
|
||||
|
@ -7,7 +7,7 @@ import java.nio.file.Path;
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.MultiverseCore;
|
||||
import com.onarandombox.MultiverseCore.api.MVConfig;
|
||||
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
|
||||
import com.onarandombox.MultiverseCore.configuration.handle.CommentedYamlConfigHandle;
|
||||
import com.onarandombox.MultiverseCore.configuration.migration.BooleanMigratorAction;
|
||||
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
|
||||
import com.onarandombox.MultiverseCore.configuration.migration.IntegerMigratorAction;
|
||||
@ -15,6 +15,7 @@ import com.onarandombox.MultiverseCore.configuration.migration.InvertBoolMigrato
|
||||
import com.onarandombox.MultiverseCore.configuration.migration.MoveMigratorAction;
|
||||
import com.onarandombox.MultiverseCore.configuration.migration.VersionMigrator;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
|
||||
import io.vavr.control.Try;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -27,13 +28,13 @@ public class MVCoreConfig implements MVConfig {
|
||||
|
||||
private final Path configPath;
|
||||
private final MVCoreConfigNodes configNodes;
|
||||
private final ConfigHandle configHandle;
|
||||
private final CommentedYamlConfigHandle configHandle;
|
||||
|
||||
@Inject
|
||||
MVCoreConfig(@NotNull MultiverseCore core, @NotNull PluginManager pluginManager) {
|
||||
this.configPath = Path.of(core.getDataFolder().getPath(), CONFIG_FILENAME);
|
||||
this.configNodes = new MVCoreConfigNodes(pluginManager);
|
||||
this.configHandle = ConfigHandle.builder(configPath)
|
||||
this.configHandle = CommentedYamlConfigHandle.builder(configPath)
|
||||
.logger(Logging.getLogger())
|
||||
.nodes(configNodes.getNodes())
|
||||
.migrator(ConfigMigrator.builder(configNodes.VERSION)
|
||||
@ -110,12 +111,12 @@ public class MVCoreConfig implements MVConfig {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
public Try<Object> getProperty(String name) {
|
||||
return configHandle.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setProperty(String name, Object value) {
|
||||
public Try<Void> setProperty(String name, Object value) {
|
||||
return configHandle.set(name, value);
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
package com.onarandombox.MultiverseCore.config;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.MVCommentedNode;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.MVValueNode;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ConfigHeaderNode;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ConfigNode;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.Node;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
|
||||
import com.onarandombox.MultiverseCore.event.MVDebugModeEvent;
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.CommentedNode;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
class MVCoreConfigNodes {
|
||||
|
||||
@ -23,12 +22,12 @@ class MVCoreConfigNodes {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private <N extends CommentedNode> N node(N node) {
|
||||
private <N extends Node> N node(N node) {
|
||||
nodes.add(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
private final MVCommentedNode HEADER = node(MVCommentedNode.builder("world") // TODO hacky way to get the header to the top of the file
|
||||
private final ConfigHeaderNode HEADER = node(ConfigHeaderNode.builder("world") // TODO hacky way to get the header to the top of the file
|
||||
.comment("####################################################################################################")
|
||||
.comment("# #")
|
||||
.comment("# █▀▄▀█ █░█ █░░ ▀█▀ █ █░█ █▀▀ █▀█ █▀ █▀▀ █▀▀ █▀█ █▀█ █▀▀ #")
|
||||
@ -52,12 +51,12 @@ class MVCoreConfigNodes {
|
||||
.comment("")
|
||||
.build());
|
||||
|
||||
// private final MVCommentedNode WORLD_HEADER = node(MVCommentedNode.builder("world")
|
||||
// private final ConfigHeaderNode WORLD_HEADER = node(ConfigHeaderNode.builder("world")
|
||||
// .comment("")
|
||||
// .comment("")
|
||||
// .build());
|
||||
|
||||
public final MVValueNode<Boolean> ENFORCE_ACCESS = node(MVValueNode.builder("world.enforce-access", Boolean.class)
|
||||
public final ConfigNode<Boolean> ENFORCE_ACCESS = node(ConfigNode.builder("world.enforce-access", Boolean.class)
|
||||
.comment("This setting will prevent players from entering worlds they don't have access to.")
|
||||
.comment("If this is set to false, players will be able to enter any world they want.")
|
||||
.comment("If this is set to true, players will only be able to enter worlds they have")
|
||||
@ -66,7 +65,7 @@ class MVCoreConfigNodes {
|
||||
.name("enforce-access")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Boolean> ENFORCE_GAMEMODE = node(MVValueNode.builder("world.enforce-gamemode", Boolean.class)
|
||||
public final ConfigNode<Boolean> ENFORCE_GAMEMODE = node(ConfigNode.builder("world.enforce-gamemode", Boolean.class)
|
||||
.comment("")
|
||||
.comment("Sets whether Multiverse will should enforce gamemode on world change.")
|
||||
.comment("If enabled, players will be forced into the gamemode of the world they are entering, unless they have")
|
||||
@ -75,14 +74,14 @@ class MVCoreConfigNodes {
|
||||
.name("enforce-gamemode")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Boolean> AUTO_PURGE_ENTITIES = node(MVValueNode.builder("world.auto-purge-entities", Boolean.class)
|
||||
public final ConfigNode<Boolean> AUTO_PURGE_ENTITIES = node(ConfigNode.builder("world.auto-purge-entities", Boolean.class)
|
||||
.comment("")
|
||||
.comment("Sets whether Multiverse will purge mobs and entities with be automatically.")
|
||||
.defaultValue(false)
|
||||
.name("auto-purge-entities")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Boolean> TELEPORT_INTERCEPT = node(MVValueNode.builder("world.teleport-intercept", Boolean.class)
|
||||
public final ConfigNode<Boolean> TELEPORT_INTERCEPT = node(ConfigNode.builder("world.teleport-intercept", Boolean.class)
|
||||
.comment("")
|
||||
.comment("If this is set to true, Multiverse will enforce access permissions for all teleportation,")
|
||||
.comment("including teleportation from other plugins.")
|
||||
@ -90,12 +89,12 @@ class MVCoreConfigNodes {
|
||||
.name("teleport-intercept")
|
||||
.build());
|
||||
|
||||
private final MVCommentedNode SPAWN_HEADER = node(MVCommentedNode.builder("spawn")
|
||||
private final ConfigHeaderNode SPAWN_HEADER = node(ConfigHeaderNode.builder("spawn")
|
||||
.comment("")
|
||||
.comment("")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Boolean> FIRST_SPAWN_OVERRIDE = node(MVValueNode.builder("spawn.first-spawn-override", Boolean.class)
|
||||
public final ConfigNode<Boolean> FIRST_SPAWN_OVERRIDE = node(ConfigNode.builder("spawn.first-spawn-override", Boolean.class)
|
||||
.comment("Sets whether Multiverse will override the first spawn location of a world.")
|
||||
.comment("If enabled, Multiverse will set the first spawn location of a world to the spawn location of the world.")
|
||||
.comment("If disabled, it will default to server.properties settings.")
|
||||
@ -103,7 +102,7 @@ class MVCoreConfigNodes {
|
||||
.name("first-spawn-override")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<String> FIRST_SPAWN_LOCATION = node(MVValueNode.builder("spawn.first-spawn-location", String.class)
|
||||
public final ConfigNode<String> FIRST_SPAWN_LOCATION = node(ConfigNode.builder("spawn.first-spawn-location", String.class)
|
||||
.comment("")
|
||||
.comment("Sets the world that Multiverse will use as the location for players that first join the server.")
|
||||
.comment("This only applies if first-spawn-override is set to true.")
|
||||
@ -111,19 +110,19 @@ class MVCoreConfigNodes {
|
||||
.name("first-spawn-location")
|
||||
.build());
|
||||
|
||||
private final MVCommentedNode PORTAL_HEADER = node(MVCommentedNode.builder("portal")
|
||||
private final ConfigHeaderNode PORTAL_HEADER = node(ConfigHeaderNode.builder("portal")
|
||||
.comment("")
|
||||
.comment("")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Boolean> USE_CUSTOM_PORTAL_SEARCH = node(MVValueNode.builder("portal.use-custom-portal-search", Boolean.class)
|
||||
public final ConfigNode<Boolean> USE_CUSTOM_PORTAL_SEARCH = node(ConfigNode.builder("portal.use-custom-portal-search", Boolean.class)
|
||||
.comment("This config option defines whether or not Multiverse should interfere with's Bukkit's default portal search radius.")
|
||||
.comment("Setting it to false would mean you want to simply let Bukkit decides the search radius itself.")
|
||||
.defaultValue(false)
|
||||
.name("use-custom-portal-search")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Integer> CUSTOM_PORTAL_SEARCH_RADIUS = node(MVValueNode.builder("portal.custom-portal-search-radius", Integer.class)
|
||||
public final ConfigNode<Integer> CUSTOM_PORTAL_SEARCH_RADIUS = node(ConfigNode.builder("portal.custom-portal-search-radius", Integer.class)
|
||||
.comment("")
|
||||
.comment("This config option defines the search radius Multiverse should use when searching for a portal.")
|
||||
.comment("This only applies if use-custom-portal-search is set to true.")
|
||||
@ -132,19 +131,19 @@ class MVCoreConfigNodes {
|
||||
.validator(value -> value >= 0)
|
||||
.build());
|
||||
|
||||
private final MVCommentedNode MESSAGING_HEADER = node(MVCommentedNode.builder("messaging")
|
||||
private final ConfigHeaderNode MESSAGING_HEADER = node(ConfigHeaderNode.builder("messaging")
|
||||
.comment("")
|
||||
.comment("")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Boolean> ENABLE_CHAT_PREFIX = node(MVValueNode.builder("messaging.enable-chat-prefix", Boolean.class)
|
||||
public final ConfigNode<Boolean> ENABLE_CHAT_PREFIX = node(ConfigNode.builder("messaging.enable-chat-prefix", Boolean.class)
|
||||
.comment("This config option defines whether or not Multiverse should prefix the chat with the world name.")
|
||||
.comment("This only applies if use-custom-portal-search is set to true.")
|
||||
.defaultValue(false)
|
||||
.name("enable-chat-prefix")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<String> CHAT_PREFIX_FORMAT = node(MVValueNode.builder("messaging.chat-prefix-format", String.class)
|
||||
public final ConfigNode<String> CHAT_PREFIX_FORMAT = node(ConfigNode.builder("messaging.chat-prefix-format", String.class)
|
||||
.comment("")
|
||||
.comment("This config option defines the format Multiverse should use when prefixing the chat with the world name.")
|
||||
.comment("This only applies if enable-chat-prefix is set to true.")
|
||||
@ -152,7 +151,7 @@ class MVCoreConfigNodes {
|
||||
.name("chat-prefix-format")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Boolean> REGISTER_PAPI_HOOK = node(MVValueNode.builder("messaging.register-papi-hook", Boolean.class)
|
||||
public final ConfigNode<Boolean> REGISTER_PAPI_HOOK = node(ConfigNode.builder("messaging.register-papi-hook", Boolean.class)
|
||||
.comment("")
|
||||
.comment("This config option defines whether or not Multiverse should register the PlaceholderAPI hook.")
|
||||
.comment("This only applies if PlaceholderAPI is installed.")
|
||||
@ -160,12 +159,12 @@ class MVCoreConfigNodes {
|
||||
.name("register-papi-hook")
|
||||
.build());
|
||||
|
||||
private final MVCommentedNode MISC_HEADER = node(MVCommentedNode.builder("misc")
|
||||
private final ConfigHeaderNode MISC_HEADER = node(ConfigHeaderNode.builder("misc")
|
||||
.comment("")
|
||||
.comment("")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Integer> GLOBAL_DEBUG = node(MVValueNode.builder("misc.global-debug", Integer.class)
|
||||
public final ConfigNode<Integer> GLOBAL_DEBUG = node(ConfigNode.builder("misc.global-debug", Integer.class)
|
||||
.comment("This is our debug flag to help identify issues with Multiverse.")
|
||||
.comment("If you are having issues with Multiverse, please set this to 3 and then post your log to pastebin.com")
|
||||
.comment("Otherwise, there's no need to touch this. If not instructed by a wiki page or developer.")
|
||||
@ -185,7 +184,7 @@ class MVCoreConfigNodes {
|
||||
})
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Boolean> SILENT_START = node(MVValueNode.builder("misc.silent-start", Boolean.class)
|
||||
public final ConfigNode<Boolean> SILENT_START = node(ConfigNode.builder("misc.silent-start", Boolean.class)
|
||||
.comment("")
|
||||
.comment("If true, the startup console messages will no longer show.")
|
||||
.defaultValue(false)
|
||||
@ -193,14 +192,14 @@ class MVCoreConfigNodes {
|
||||
.onSetValue((oldValue, newValue) -> Logging.setShowingConfig(!newValue))
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Boolean> SHOW_DONATION_MESSAGE = node(MVValueNode.builder("misc.show-donation-message", Boolean.class)
|
||||
public final ConfigNode<Boolean> SHOW_DONATION_MESSAGE = node(ConfigNode.builder("misc.show-donation-message", Boolean.class)
|
||||
.comment("")
|
||||
.comment("If you don't want to donate, you can set this to false and Multiverse will stop nagging you.")
|
||||
.defaultValue(true)
|
||||
.name("show-donation-message")
|
||||
.build());
|
||||
|
||||
public final MVValueNode<Double> VERSION = node(MVValueNode.builder("version", Double.class)
|
||||
public final ConfigNode<Double> VERSION = node(ConfigNode.builder("version", Double.class)
|
||||
.comment("")
|
||||
.comment("")
|
||||
.comment("This just signifies the version number so we can see what version of config you have.")
|
||||
|
@ -1,360 +0,0 @@
|
||||
package com.onarandombox.MultiverseCore.configuration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.EnhancedValueNode;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
|
||||
import io.github.townyadvanced.commentedconfiguration.CommentedConfiguration;
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.CommentedNode;
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.TypedValueNode;
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.ValueNode;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A class that makes use of CommentedConfiguration to provide a simple way to load and save with node objects.
|
||||
*/
|
||||
public class ConfigHandle {
|
||||
|
||||
/**
|
||||
* A builder class for creating a ConfigHandle.
|
||||
*
|
||||
* @param configPath The path to the configuration file in string.
|
||||
* @return A new Builder instance.
|
||||
*/
|
||||
public static Builder builder(String configPath) {
|
||||
return new Builder(configPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder class for creating a ConfigHandle.
|
||||
*
|
||||
* @param configPath The path to the configuration file.
|
||||
* @return A new Builder instance.
|
||||
*/
|
||||
public static Builder builder(Path configPath) {
|
||||
return new Builder(configPath);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected final Path configPath;
|
||||
@Nullable
|
||||
protected final Logger logger;
|
||||
@NotNull
|
||||
protected final NodeGroup nodes;
|
||||
|
||||
protected final ConfigMigrator migrator;
|
||||
|
||||
protected CommentedConfiguration config;
|
||||
|
||||
/**
|
||||
* Creates a new MVSettings instance that makes use of CommentedConfiguration.
|
||||
*
|
||||
* @param configPath The path to the configuration file.
|
||||
* @param logger The Logger to use for error messages.
|
||||
* @param nodes All the node path and values for the configuration.
|
||||
* @param migrator The migrator to use for migrating the configuration.
|
||||
*/
|
||||
protected ConfigHandle(@NotNull Path configPath, @Nullable Logger logger, @NotNull NodeGroup nodes, ConfigMigrator migrator) {
|
||||
this.configPath = configPath;
|
||||
this.nodes = nodes;
|
||||
this.logger = logger;
|
||||
this.migrator = migrator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configuration.
|
||||
*
|
||||
* @return True if the configuration was loaded successfully, false otherwise.
|
||||
*/
|
||||
public boolean load() {
|
||||
if (!createConfigFile()) {
|
||||
return false;
|
||||
}
|
||||
this.config = new CommentedConfiguration(configPath, logger);
|
||||
if (!config.load()) {
|
||||
return false;
|
||||
}
|
||||
migrateConfig();
|
||||
parseAllNodes();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new config file if file does not exist
|
||||
*
|
||||
* @return True if file exist or created successfully, otherwise false.
|
||||
*/
|
||||
protected boolean createConfigFile() {
|
||||
File configFile = configPath.toFile();
|
||||
if (configFile.exists()) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
if (!configFile.createNewFile()) {
|
||||
return false;
|
||||
}
|
||||
Logging.info("Created new config file: %s", configFile.getName());
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migration of the configuration based on {@link ConfigMigrator}.
|
||||
*/
|
||||
protected void migrateConfig() {
|
||||
migrator.migrate(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default node values to the configuration if they are not already present.
|
||||
*/
|
||||
protected void parseAllNodes() {
|
||||
CommentedConfiguration oldConfig = config;
|
||||
this.config = new CommentedConfiguration(configPath, logger);
|
||||
for (CommentedNode node : nodes) {
|
||||
if (node.getComments().length > 0) {
|
||||
config.addComment(node.getPath(), node.getComments());
|
||||
}
|
||||
if (node instanceof TypedValueNode typedNode) {
|
||||
if (!set(typedNode, oldConfig.getObject(node.getPath(), typedNode.getType(), typedNode.getDefaultValue()))) {
|
||||
Logging.warning("Invalid value for node: %s, resetting to default...", node.getPath());
|
||||
setDefault(typedNode);
|
||||
}
|
||||
} else if (node instanceof ValueNode valueNode) {
|
||||
if (!set(valueNode, oldConfig.get(node.getPath(), valueNode.getDefaultValue()))) {
|
||||
Logging.warning("Invalid value for node: %s, resetting to default...", node.getPath());
|
||||
setDefault(valueNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the configuration.
|
||||
*/
|
||||
public void save() {
|
||||
config.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the configuration is loaded.
|
||||
*
|
||||
* @return True if the configuration is loaded, false otherwise.
|
||||
*/
|
||||
public boolean isLoaded() {
|
||||
return config != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a node, if the node has a default value, it will be returned if the node is not found.
|
||||
*
|
||||
* @param node The node to get the value of.
|
||||
* @return The value of the node.
|
||||
*/
|
||||
public Object get(@NotNull ValueNode node) {
|
||||
return config.get(node.getPath(), node.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the node by name.
|
||||
*
|
||||
* @param name The name of the node to get the value of.
|
||||
* @return The value of the node.
|
||||
*/
|
||||
public Object get(@NotNull String name) {
|
||||
return nodes.findNode(name)
|
||||
.map(node -> (node instanceof ValueNode) ? get((ValueNode) node) : null)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a node, if the node has a default value, it will be returned if the node is not found.
|
||||
*
|
||||
* @param node The node to get the value of.
|
||||
* @param type The type of the node value.
|
||||
* @param <T> The type of the node value.
|
||||
* @return The value of the node.
|
||||
*/
|
||||
public <T> T get(@NotNull ValueNode node, Class<T> type) {
|
||||
return config.getObject(node.getPath(), type, (T) node.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a node, if the node has a default value, it will be returned if the node is not found.
|
||||
*
|
||||
* @param node The node to get the value of.
|
||||
* @param <T> The type of the node value.
|
||||
* @return The value of the node.
|
||||
*/
|
||||
public <T> T get(@NotNull TypedValueNode<T> node) {
|
||||
return config.getObject(node.getPath(), node.getType(), node.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the node by name.
|
||||
*
|
||||
* @param name The name of the node to set the value of.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
public boolean set(@NotNull String name, Object value) {
|
||||
return nodes.findNode(name)
|
||||
.map(node -> node instanceof ValueNode && set((ValueNode) node, value))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a node, if the validator is not null, it will be tested first.
|
||||
*
|
||||
* @param node The node to set the value of.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
public boolean set(@NotNull ValueNode node, Object value) {
|
||||
if (node instanceof TypedValueNode typedValueNode) {
|
||||
return set(typedValueNode, value);
|
||||
}
|
||||
config.set(node.getPath(), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a node, if the validator is not null, it will be tested first.
|
||||
*
|
||||
* @param node The node to set the value of.
|
||||
* @param value The value to set.
|
||||
* @param <T> The type of the node value.
|
||||
*/
|
||||
public <T> boolean set(@NotNull TypedValueNode<T> node, T value) {
|
||||
if (node instanceof EnhancedValueNode<T> enhancedValueNode) {
|
||||
return set(enhancedValueNode, value);
|
||||
}
|
||||
config.set(node.getPath(), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a node, if the validator is not null, it will be tested first.
|
||||
*
|
||||
* @param node The node to set the value of.
|
||||
* @param value The value to set.
|
||||
* @return True if the value was set, false otherwise.
|
||||
* @param <T> The type of the node value.
|
||||
*/
|
||||
public <T> boolean set(@NotNull EnhancedValueNode<T> node, T value) {
|
||||
if (!node.isValid(value)) {
|
||||
return false;
|
||||
}
|
||||
T oldValue = get(node);
|
||||
config.set(node.getPath(), value);
|
||||
node.onSetValue(oldValue, get(node));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value of a node.
|
||||
*
|
||||
* @param node The node to set the default value of.
|
||||
*/
|
||||
public void setDefault(@NotNull ValueNode node) {
|
||||
config.set(node.getPath(), node.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the inner configuration object.
|
||||
*
|
||||
* @return The configuration object.
|
||||
*/
|
||||
public @NotNull CommentedConfiguration getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path of the configuration file.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private final Path configPath;
|
||||
private Logger logger;
|
||||
private NodeGroup nodes;
|
||||
|
||||
private ConfigMigrator migrator;
|
||||
|
||||
/**
|
||||
* Creates a new builder.
|
||||
*
|
||||
* @param configPath The path of the configuration file.
|
||||
*/
|
||||
public Builder(String configPath) {
|
||||
this.configPath = Path.of(configPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder.
|
||||
*
|
||||
* @param configPath The path of the configuration file.
|
||||
*/
|
||||
public Builder(Path configPath) {
|
||||
this.configPath = configPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logger to use.
|
||||
*
|
||||
* @param plugin The plugin to get the logger from.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder logger(@NotNull Plugin plugin) {
|
||||
return logger(plugin.getLogger());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logger to use.
|
||||
*
|
||||
* @param logger The logger to use.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder logger(@Nullable Logger logger) {
|
||||
this.logger = logger;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the nodes to use.
|
||||
*
|
||||
* @param nodes The nodes to use.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder nodes(@Nullable NodeGroup nodes) {
|
||||
this.nodes = nodes;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the migrator to use.
|
||||
*
|
||||
* @param migrator The migrator to use.
|
||||
* @return The builder.
|
||||
*/
|
||||
public Builder migrator(@Nullable ConfigMigrator migrator) {
|
||||
this.migrator = migrator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the settings.
|
||||
*
|
||||
* @return The built settings.
|
||||
*/
|
||||
public ConfigHandle build() {
|
||||
return new ConfigHandle(configPath, logger, nodes, migrator);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.handle;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.CommentedNode;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
|
||||
import io.github.townyadvanced.commentedconfiguration.CommentedConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Configuration handle for commented YAML files.
|
||||
*/
|
||||
public class CommentedYamlConfigHandle extends FileConfigHandle<CommentedConfiguration> {
|
||||
|
||||
/**
|
||||
* Creates a new builder for a {@link CommentedYamlConfigHandle}.
|
||||
*
|
||||
* @param configPath The path to the config file.
|
||||
* @return The builder.
|
||||
*/
|
||||
public static @NotNull Builder builder(@NotNull Path configPath) {
|
||||
return new Builder(configPath);
|
||||
}
|
||||
|
||||
protected CommentedYamlConfigHandle(@NotNull Path configPath, @Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
|
||||
super(configPath, logger, nodes, migrator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean loadConfigObject() {
|
||||
config = new CommentedConfiguration(configPath, logger);
|
||||
return config.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void setUpNodes() {
|
||||
if (nodes == null || nodes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CommentedConfiguration oldConfig = config;
|
||||
this.config = new CommentedConfiguration(configPath, logger);
|
||||
|
||||
nodes.forEach(node -> {
|
||||
if (node instanceof CommentedNode typedNode) {
|
||||
if (typedNode.getComments().length > 0) {
|
||||
config.addComment(typedNode.getPath(), typedNode.getComments());
|
||||
}
|
||||
}
|
||||
if (node instanceof ValueNode valueNode) {
|
||||
set(valueNode, oldConfig.getObject(valueNode.getPath(), valueNode.getType(), valueNode.getDefaultValue())).onFailure(e -> {
|
||||
Logging.warning("Failed to set node " + valueNode.getPath() + " to " + valueNode.getDefaultValue());
|
||||
setDefault(valueNode);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean save() {
|
||||
config.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class Builder extends FileConfigHandle.Builder<CommentedConfiguration, Builder> {
|
||||
|
||||
protected Builder(@NotNull Path configPath) {
|
||||
super(configPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull CommentedYamlConfigHandle build() {
|
||||
return new CommentedYamlConfigHandle(configPath, logger, nodes, migrator);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.handle;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ConfigNodeNotFoundException;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
|
||||
import com.onarandombox.MultiverseCore.exceptions.MultiverseException;
|
||||
import io.vavr.control.Try;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Generic configuration handle for file based configurations.
|
||||
* @param <C> The configuration type.
|
||||
*/
|
||||
abstract class FileConfigHandle<C extends FileConfiguration> {
|
||||
|
||||
protected final @NotNull Path configPath;
|
||||
protected final @NotNull File configFile;
|
||||
protected final @Nullable Logger logger;
|
||||
protected final @Nullable NodeGroup nodes;
|
||||
protected final @Nullable ConfigMigrator migrator;
|
||||
|
||||
protected C config;
|
||||
|
||||
protected FileConfigHandle(@NotNull Path configPath, @Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
|
||||
this.configPath = configPath;
|
||||
this.configFile = configPath.toFile();
|
||||
this.logger = logger;
|
||||
this.nodes = nodes;
|
||||
this.migrator = migrator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configuration.
|
||||
*
|
||||
* @return True if the configuration was loaded successfully, false otherwise.
|
||||
*/
|
||||
public boolean load() {
|
||||
if (!createConfigFile()) {
|
||||
Logging.severe("Failed to create config file: %s", configFile.getName());
|
||||
return false;
|
||||
}
|
||||
if (!loadConfigObject()) {
|
||||
Logging.severe("Failed to load config file: %s", configFile.getName());
|
||||
return false;
|
||||
}
|
||||
migrateConfig();
|
||||
setUpNodes();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new config file if file does not exist
|
||||
*
|
||||
* @return True if file exist or created successfully, otherwise false.
|
||||
*/
|
||||
protected boolean createConfigFile() {
|
||||
if (configFile.exists()) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
if (!configFile.createNewFile()) {
|
||||
return false;
|
||||
}
|
||||
Logging.info("Created new config file: %s", configFile.getName());
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configuration object.
|
||||
*
|
||||
* @return True if the configuration was loaded successfully, false otherwise.
|
||||
*/
|
||||
protected abstract boolean loadConfigObject();
|
||||
|
||||
/**
|
||||
* Migrates the configuration.
|
||||
*/
|
||||
protected void migrateConfig() {
|
||||
if (migrator != null) {
|
||||
migrator.migrate(config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the nodes.
|
||||
*/
|
||||
protected abstract void setUpNodes();
|
||||
|
||||
/**
|
||||
* Saves the configuration.
|
||||
*/
|
||||
public abstract boolean save();
|
||||
|
||||
/**
|
||||
* Checks if the configuration is loaded.
|
||||
*
|
||||
* @return True if the configuration is loaded, false otherwise.
|
||||
*/
|
||||
public boolean isLoaded() {
|
||||
return config != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration.
|
||||
*
|
||||
* @return The configuration.
|
||||
*/
|
||||
public C getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a node, if the node has a default value, it will be returned if the node is not found.
|
||||
* @param name The name of the node.
|
||||
* @return The value of the node.
|
||||
*/
|
||||
public Try<Object> get(@Nullable String name) {
|
||||
return nodes.findNode(name, ValueNode.class)
|
||||
.toTry(() -> new ConfigNodeNotFoundException(name))
|
||||
.map(node -> get((ValueNode<Object>) node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a node, if the node has a default value, it will be returned if the node is not found.
|
||||
*
|
||||
* @param node The node to get the value of.
|
||||
* @return The value of the node.
|
||||
*/
|
||||
public <T> T get(@NotNull ValueNode<T> node) {
|
||||
return config.getObject(node.getPath(), node.getType(), node.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a node, if the validator is not null, it will be tested first.
|
||||
*
|
||||
* @param name The name of the node.
|
||||
* @param value The value to set.
|
||||
* @return True if the value was set, false otherwise.
|
||||
*/
|
||||
public Try<Void> set(@Nullable String name, Object value) {
|
||||
return nodes.findNode(name, ValueNode.class)
|
||||
.toTry(() -> new ConfigNodeNotFoundException(name))
|
||||
.flatMap(node -> set(node, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a node, if the validator is not null, it will be tested first.
|
||||
*
|
||||
* @param node The node to set the value of.
|
||||
* @param value The value to set.
|
||||
* @return True if the value was set, false otherwise.
|
||||
* @param <T> The type of the node value.
|
||||
*/
|
||||
public <T> Try<Void> set(@NotNull ValueNode<T> node, T value) {
|
||||
if (!node.validate(value)) {
|
||||
return Try.failure(new MultiverseException("Validation failed", null)); // TODO replace validation
|
||||
}
|
||||
T oldValue = get(node);
|
||||
config.set(node.getPath(), value);
|
||||
node.onSetValue(oldValue, get(node));
|
||||
return Try.success(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value of a node.
|
||||
*
|
||||
* @param node The node to set the default value of.
|
||||
*/
|
||||
public void setDefault(@NotNull ValueNode node) {
|
||||
config.set(node.getPath(), node.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link FileConfigHandle}.
|
||||
*
|
||||
* @param <C> The configuration type.
|
||||
* @param <B> The builder type.
|
||||
*/
|
||||
public static abstract class Builder<C extends FileConfiguration, B extends Builder<C, B>> {
|
||||
|
||||
protected @NotNull Path configPath;
|
||||
protected @Nullable Logger logger;
|
||||
protected @Nullable NodeGroup nodes;
|
||||
protected @Nullable ConfigMigrator migrator;
|
||||
|
||||
protected Builder(@NotNull Path configPath) {
|
||||
this.configPath = configPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logger.
|
||||
*
|
||||
* @param logger The logger.
|
||||
* @return The builder.
|
||||
*/
|
||||
public B logger(@Nullable Logger logger) {
|
||||
this.logger = logger;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logger.
|
||||
*
|
||||
* @param plugin The plugin to get the logger from.
|
||||
* @return The builder.
|
||||
*/
|
||||
public B logger(Plugin plugin) {
|
||||
this.logger = plugin.getLogger();
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the nodes.
|
||||
*
|
||||
* @param nodes The nodes.
|
||||
* @return The builder.
|
||||
*/
|
||||
public B nodes(@Nullable NodeGroup nodes) {
|
||||
this.nodes = nodes;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the migrator.
|
||||
*
|
||||
* @param migrator The migrator.
|
||||
* @return The builder.
|
||||
*/
|
||||
public B migrator(@Nullable ConfigMigrator migrator) {
|
||||
this.migrator = migrator;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the configuration handle.
|
||||
*
|
||||
* @return The configuration handle.
|
||||
*/
|
||||
public abstract @NotNull FileConfigHandle<C> build();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected B self() {
|
||||
return (B) this;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.handle;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Configuration handle for YAML files.
|
||||
*/
|
||||
public class YamlConfigHandle extends FileConfigHandle<YamlConfiguration> {
|
||||
|
||||
/**
|
||||
* Creates a new builder for {@link YamlConfigHandle}.
|
||||
*
|
||||
* @param configPath The path to the config file.
|
||||
* @return The builder.
|
||||
*/
|
||||
public static @NotNull Builder<? extends Builder> builder(@NotNull Path configPath) {
|
||||
return new Builder<>(configPath);
|
||||
}
|
||||
|
||||
protected YamlConfigHandle(@NotNull Path configPath, @Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
|
||||
super(configPath, logger, nodes, migrator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean loadConfigObject() {
|
||||
config = new YamlConfiguration();
|
||||
try {
|
||||
config.load(configFile);
|
||||
} catch (IOException | InvalidConfigurationException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void setUpNodes() {
|
||||
if (nodes == null || nodes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfiguration oldConfig = config;
|
||||
config = new YamlConfiguration();
|
||||
nodes.forEach(node -> {
|
||||
if (node instanceof ValueNode valueNode) {
|
||||
set(valueNode, oldConfig.getObject(valueNode.getPath(), valueNode.getType(), valueNode.getDefaultValue()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean save() {
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link YamlConfigHandle}.
|
||||
* @param <B> The type of the builder.
|
||||
*/
|
||||
public static class Builder<B extends Builder<B>> extends FileConfigHandle.Builder<YamlConfiguration, B> {
|
||||
|
||||
protected Builder(@NotNull Path configPath) {
|
||||
super(configPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull YamlConfigHandle build() {
|
||||
return new YamlConfigHandle(configPath, logger, nodes, migrator);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package com.onarandombox.MultiverseCore.configuration.migration;
|
||||
|
||||
import co.aikar.commands.ACFUtil;
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
/**
|
||||
* Single migrator action that converts a string value to a boolean.
|
||||
@ -20,9 +20,9 @@ public class BooleanMigratorAction implements MigratorAction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(ConfigHandle settings) {
|
||||
settings.getConfig().set(path, ACFUtil.isTruthy(settings.getConfig().getString(path, "")));
|
||||
Logging.info("Converted %s to boolean %s", path, settings.getConfig().getBoolean(path));
|
||||
public void migrate(FileConfiguration config) {
|
||||
config.set(path, ACFUtil.isTruthy(config.getString(path, "")));
|
||||
Logging.info("Converted %s to boolean %s", path, config.getBoolean(path));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
|
||||
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.TypedValueNode;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
/**
|
||||
* Helper class for migrating configs to the latest config version.
|
||||
@ -19,14 +20,14 @@ public class ConfigMigrator {
|
||||
* Default value should be the current latest version number.
|
||||
* @return The builder instance.
|
||||
*/
|
||||
public static Builder builder(TypedValueNode<Double> versionNode) {
|
||||
public static Builder builder(ValueNode<Double> versionNode) {
|
||||
return new Builder(versionNode);
|
||||
}
|
||||
|
||||
private final TypedValueNode<Double> versionNode;
|
||||
private final ValueNode<Double> versionNode;
|
||||
private final List<VersionMigrator> versionMigrators;
|
||||
|
||||
protected ConfigMigrator(TypedValueNode<Double> versionNode, List<VersionMigrator> versionMigrators) {
|
||||
protected ConfigMigrator(ValueNode<Double> versionNode, List<VersionMigrator> versionMigrators) {
|
||||
this.versionNode = versionNode;
|
||||
this.versionMigrators = versionMigrators;
|
||||
}
|
||||
@ -34,25 +35,25 @@ public class ConfigMigrator {
|
||||
/**
|
||||
* Migrates the config to the latest version if necessary.
|
||||
*
|
||||
* @param settings The target settings instance to migrate.
|
||||
* @param config The target settings instance to migrate.
|
||||
*/
|
||||
public void migrate(ConfigHandle settings) {
|
||||
double versionNumber = settings.getConfig().getDouble(versionNode.getPath());
|
||||
public void migrate(FileConfiguration config) {
|
||||
double versionNumber = config.getDouble(versionNode.getPath());
|
||||
for (VersionMigrator versionMigrator : versionMigrators) {
|
||||
if (versionNumber < versionMigrator.getVersion()) {
|
||||
Logging.info("Migrating config from version %s to %s...", versionNumber, versionMigrator.getVersion());
|
||||
versionMigrator.migrate(settings);
|
||||
versionMigrator.migrate(config);
|
||||
}
|
||||
}
|
||||
// Set the version number to the latest version number
|
||||
settings.setDefault(versionNode);
|
||||
config.set(versionNode.getPath(), versionNode.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for a ConfigMigrator.
|
||||
*/
|
||||
public static class Builder {
|
||||
private final TypedValueNode<Double> versionNode;
|
||||
private final ValueNode<Double> versionNode;
|
||||
private final List<VersionMigrator> versionMigrators;
|
||||
|
||||
/**
|
||||
@ -61,7 +62,7 @@ public class ConfigMigrator {
|
||||
* @param versionNode The node that stores the version number of the config.
|
||||
* Default value should be the current latest version number.
|
||||
*/
|
||||
public Builder(TypedValueNode<Double> versionNode) {
|
||||
public Builder(ValueNode<Double> versionNode) {
|
||||
this.versionNode = versionNode;
|
||||
this.versionMigrators = new ArrayList<>();
|
||||
}
|
||||
|
@ -2,8 +2,7 @@ package com.onarandombox.MultiverseCore.configuration.migration;
|
||||
|
||||
import co.aikar.commands.ACFUtil;
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
|
||||
import org.bukkit.util.NumberConversions;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
/**
|
||||
* Single migrator action that converts a string value to an integer.
|
||||
@ -21,8 +20,8 @@ public class IntegerMigratorAction implements MigratorAction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(ConfigHandle settings) {
|
||||
settings.getConfig().set(path, ACFUtil.parseInt(settings.getConfig().getString(path)));
|
||||
Logging.info("Converted %s to integer %s", path, settings.getConfig().getInt(path));
|
||||
public void migrate(FileConfiguration config) {
|
||||
config.set(path, ACFUtil.parseInt(config.getString(path)));
|
||||
Logging.info("Converted %s to integer %s", path, config.getInt(path));
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.migration;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
/**
|
||||
* Single migrator action that inverts a boolean value for a given path.
|
||||
@ -28,9 +28,9 @@ public class InvertBoolMigratorAction implements MigratorAction {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void migrate(ConfigHandle settings) {
|
||||
boolean boolValue = !settings.getConfig().getBoolean(path);
|
||||
settings.getConfig().set(path, boolValue);
|
||||
public void migrate(FileConfiguration config) {
|
||||
boolean boolValue = !config.getBoolean(path);
|
||||
config.set(path, boolValue);
|
||||
Logging.info("Inverted %s to boolean %s", path, boolValue);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.migration;
|
||||
|
||||
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
/**
|
||||
* A migrator action is a single action that is performed when migrating a config.
|
||||
@ -10,7 +10,7 @@ public interface MigratorAction {
|
||||
/**
|
||||
* Performs the migration action.
|
||||
*
|
||||
* @param settings The target settings instance to migrate.
|
||||
* @param config The target settings instance to migrate.
|
||||
*/
|
||||
void migrate(ConfigHandle settings);
|
||||
void migrate(FileConfiguration config);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package com.onarandombox.MultiverseCore.configuration.migration;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
/**
|
||||
* Single migrator action that moves a value from one path to another.
|
||||
@ -33,11 +33,11 @@ public class MoveMigratorAction implements MigratorAction {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void migrate(ConfigHandle settings) {
|
||||
Optional.ofNullable(settings.getConfig().get(fromPath))
|
||||
public void migrate(FileConfiguration config) {
|
||||
Optional.ofNullable(config.get(fromPath))
|
||||
.ifPresent(value -> {
|
||||
settings.getConfig().set(toPath, value);
|
||||
settings.getConfig().set(fromPath, null);
|
||||
config.set(toPath, value);
|
||||
config.set(fromPath, null);
|
||||
Logging.config("Moved path %s to %s", fromPath, toPath);
|
||||
});
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package com.onarandombox.MultiverseCore.configuration.migration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
/**
|
||||
* A version migrator is a collection of migrator actions that are performed when migrating a config to a specific version.
|
||||
@ -31,10 +31,10 @@ public class VersionMigrator {
|
||||
/**
|
||||
* Performs all the migrator actions.
|
||||
*
|
||||
* @param settings The target settings instance to migrate.
|
||||
* @param config The target settings instance to migrate.
|
||||
*/
|
||||
public void migrate(ConfigHandle settings) {
|
||||
actions.forEach(action -> action.migrate(settings));
|
||||
public void migrate(FileConfiguration config) {
|
||||
actions.forEach(action -> action.migrate(config));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface CommentedNode extends Node {
|
||||
|
||||
/**
|
||||
* Gets the comment of the node.
|
||||
*
|
||||
* @return The comment of the node.
|
||||
*/
|
||||
@NotNull String[] getComments();
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A node that represents a header without any value.
|
||||
*/
|
||||
public class ConfigHeaderNode implements CommentedNode {
|
||||
|
||||
/**
|
||||
* Creates a new builder for a {@link ConfigHeaderNode}.
|
||||
*
|
||||
* @param path The path of the node.
|
||||
* @return The new builder.
|
||||
*/
|
||||
public static @NotNull Builder<? extends Builder<?>> builder(String path) {
|
||||
return new Builder<>(path);
|
||||
}
|
||||
|
||||
private final @NotNull String path;
|
||||
private final @NotNull String[] comments;
|
||||
|
||||
protected ConfigHeaderNode(@NotNull String path, @NotNull String[] comments) {
|
||||
this.path = path;
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull String[] getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public static class Builder<B extends Builder<B>> {
|
||||
|
||||
protected final @NotNull String path;
|
||||
protected final @NotNull List<String> comments;
|
||||
|
||||
public Builder(@NotNull String path) {
|
||||
this.path = path;
|
||||
this.comments = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comment line to the node.
|
||||
*
|
||||
* @param comment The comment to add.
|
||||
* @return This builder.
|
||||
*/
|
||||
public @NotNull B comment(@NotNull String comment) {
|
||||
if (!Strings.isNullOrEmpty(comment) && !comment.startsWith("#")) {
|
||||
comment = "# " + comment;
|
||||
}
|
||||
comments.add(comment);
|
||||
return (B) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the node.
|
||||
*
|
||||
* @return The built node.
|
||||
*/
|
||||
public @NotNull ConfigHeaderNode build() {
|
||||
return new ConfigHeaderNode(path, comments.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +1,64 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.vavr.control.Option;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Implementation of {@link EnhancedValueNode}.
|
||||
* @param <T> The type of the value.
|
||||
* A node that contains a value.
|
||||
* @param <T> The type of the value.
|
||||
*/
|
||||
public class MVValueNode<T> extends MVCommentedNode implements EnhancedValueNode<T> {
|
||||
public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
|
||||
|
||||
/**
|
||||
* Creates a new builder for a {@link MVValueNode}.
|
||||
* Creates a new builder for a {@link ConfigNode}.
|
||||
*
|
||||
* @param path The path of the node.
|
||||
* @param type The type of the value.
|
||||
* @return The new builder.
|
||||
* @param <T> The type of the value.
|
||||
*/
|
||||
public static <T> Builder<T, ? extends Builder> builder(String path, Class<T> type) {
|
||||
return new Builder<>(path, type);
|
||||
public static @NotNull <T> ConfigNode.Builder<T, ? extends ConfigNode.Builder<T, ?>> builder(
|
||||
@NotNull String path,
|
||||
@NotNull Class<T> type
|
||||
) {
|
||||
return new ConfigNode.Builder<>(path, type);
|
||||
}
|
||||
|
||||
protected final Class<T> type;
|
||||
protected final T defaultValue;
|
||||
protected final String name;
|
||||
protected final Function<T, Boolean> validator;
|
||||
protected final BiConsumer<T, T> onSetValue;
|
||||
protected final @Nullable String name;
|
||||
protected final @NotNull Class<T> type;
|
||||
protected final @Nullable T defaultValue;
|
||||
protected final @Nullable Function<T, Boolean> validator;
|
||||
protected final @Nullable BiConsumer<T, T> onSetValue;
|
||||
|
||||
protected MVValueNode(String path, String[] comments, Class<T> type, T defaultValue, String name, Function<T, Boolean> validator, BiConsumer<T, T> onSetValue) {
|
||||
protected ConfigNode(
|
||||
@NotNull String path,
|
||||
@NotNull String[] comments,
|
||||
@Nullable String name,
|
||||
@NotNull Class<T> type,
|
||||
@Nullable T defaultValue,
|
||||
@Nullable Function<T, Boolean> validator,
|
||||
@Nullable BiConsumer<T, T> onSetValue
|
||||
) {
|
||||
super(path, comments);
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.defaultValue = defaultValue;
|
||||
this.name = name;
|
||||
this.validator = validator;
|
||||
this.onSetValue = onSetValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull Option<String> getName() {
|
||||
return Option.of(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ -60,15 +79,7 @@ public class MVValueNode<T> extends MVCommentedNode implements EnhancedValueNode
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Optional<String> getName() {
|
||||
return Optional.ofNullable(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isValid(T value) {
|
||||
public boolean validate(@Nullable T value) {
|
||||
if (validator != null) {
|
||||
return validator.apply(value);
|
||||
}
|
||||
@ -79,25 +90,25 @@ public class MVValueNode<T> extends MVCommentedNode implements EnhancedValueNode
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onSetValue(T oldValue, T newValue) {
|
||||
public void onSetValue(@Nullable T oldValue, @Nullable T newValue) {
|
||||
if (onSetValue != null) {
|
||||
onSetValue.accept(oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link MVValueNode}.
|
||||
* Builder for {@link ConfigNode}.
|
||||
*
|
||||
* @param <T> The type of the value.
|
||||
* @param <B> The type of the builder.
|
||||
*/
|
||||
public static class Builder<T, B extends Builder<T, B>> extends MVCommentedNode.Builder<B> {
|
||||
public static class Builder<T, B extends ConfigNode.Builder<T, B>> extends ConfigHeaderNode.Builder<B> {
|
||||
|
||||
protected final Class<T> type;
|
||||
protected T defaultValue;
|
||||
protected String name;
|
||||
protected Function<T, Boolean> validator;
|
||||
protected BiConsumer<T, T> onSetValue;
|
||||
protected @Nullable String name;
|
||||
protected @NotNull final Class<T> type;
|
||||
protected @Nullable T defaultValue;
|
||||
protected @Nullable Function<T, Boolean> validator;
|
||||
protected @Nullable BiConsumer<T, T> onSetValue;
|
||||
|
||||
/**
|
||||
* Creates a new builder.
|
||||
@ -107,8 +118,8 @@ public class MVValueNode<T> extends MVCommentedNode implements EnhancedValueNode
|
||||
*/
|
||||
protected Builder(@NotNull String path, @NotNull Class<T> type) {
|
||||
super(path);
|
||||
this.type = type;
|
||||
this.name = path;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,7 +128,7 @@ public class MVValueNode<T> extends MVCommentedNode implements EnhancedValueNode
|
||||
* @param defaultValue The default value.
|
||||
* @return This builder.
|
||||
*/
|
||||
public B defaultValue(@NotNull T defaultValue) {
|
||||
public @NotNull B defaultValue(@NotNull T defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return (B) this;
|
||||
}
|
||||
@ -128,12 +139,12 @@ public class MVValueNode<T> extends MVCommentedNode implements EnhancedValueNode
|
||||
* @param name The name of this node.
|
||||
* @return This builder.
|
||||
*/
|
||||
public B name(@Nullable String name) {
|
||||
public @NotNull B name(@Nullable String name) {
|
||||
this.name = name;
|
||||
return (B) this;
|
||||
}
|
||||
|
||||
public B validator(@Nullable Function<T, Boolean> validator) {
|
||||
public @NotNull B validator(@NotNull Function<T, Boolean> validator) {
|
||||
this.validator = validator;
|
||||
return (B) this;
|
||||
}
|
||||
@ -144,7 +155,7 @@ public class MVValueNode<T> extends MVCommentedNode implements EnhancedValueNode
|
||||
* @param onSetValue The action to be performed.
|
||||
* @return This builder.
|
||||
*/
|
||||
public B onSetValue(@Nullable BiConsumer<T, T> onSetValue) {
|
||||
public @NotNull B onSetValue(@NotNull BiConsumer<T, T> onSetValue) {
|
||||
this.onSetValue = onSetValue;
|
||||
return (B) this;
|
||||
}
|
||||
@ -153,8 +164,8 @@ public class MVValueNode<T> extends MVCommentedNode implements EnhancedValueNode
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public MVValueNode<T> build() {
|
||||
return new MVValueNode<>(path, comments.toArray(new String[0]), type, defaultValue, name, validator, onSetValue);
|
||||
public @NotNull ConfigNode<T> build() {
|
||||
return new ConfigNode<>(path, comments.toArray(new String[0]), name, type, defaultValue, validator, onSetValue);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
import com.onarandombox.MultiverseCore.exceptions.MultiverseException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.onarandombox.MultiverseCore.utils.MVCorei18n.CONFIG_NODE_NOTFOUND;
|
||||
import static com.onarandombox.MultiverseCore.utils.message.MessageReplacement.replace;
|
||||
|
||||
public class ConfigNodeNotFoundException extends MultiverseException {
|
||||
|
||||
public ConfigNodeNotFoundException(@Nullable String nodeName) {
|
||||
super(CONFIG_NODE_NOTFOUND.bundle("Config node not found: {node}", replace("{node}").with(nodeName)), null);
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.TypedValueNode;
|
||||
|
||||
/**
|
||||
* A {@link TypedValueNode} that has a name, validation, and action to be performed when the value is set.
|
||||
*
|
||||
* @param <T> The type of the node's value.
|
||||
*/
|
||||
public interface EnhancedValueNode<T> extends TypedValueNode<T> {
|
||||
/**
|
||||
* Gets the name of this node. Used for identifying the node from user input.
|
||||
*
|
||||
* @return The name of this node.
|
||||
*/
|
||||
Optional<String> getName();
|
||||
|
||||
boolean isValid(T value);
|
||||
|
||||
/**
|
||||
* Called when the value of this node is set.
|
||||
*
|
||||
* @param oldValue The old value.
|
||||
* @param newValue The new value.
|
||||
*/
|
||||
void onSetValue(T oldValue, T newValue);
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.CommentedNode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implementation of {@link CommentedNode} that allows for comments to be added to the node.
|
||||
*/
|
||||
public class MVCommentedNode implements CommentedNode {
|
||||
|
||||
/**
|
||||
* Creates a new builder for a {@link MVCommentedNode}.
|
||||
*
|
||||
* @param path The path of the node.
|
||||
* @return The new builder.
|
||||
*/
|
||||
public static Builder<Builder> builder(String path) {
|
||||
return new Builder<>(path);
|
||||
}
|
||||
|
||||
protected final String path;
|
||||
protected final String[] comments;
|
||||
|
||||
protected MVCommentedNode(String path, String[] comments) {
|
||||
this.path = path;
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public @NotNull String[] getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link MVCommentedNode}.
|
||||
*
|
||||
* @param <B> The type of the builder.
|
||||
*/
|
||||
public static class Builder<B extends Builder> {
|
||||
protected final String path;
|
||||
protected final List<String> comments;
|
||||
|
||||
/**
|
||||
* Creates a new builder for a {@link MVCommentedNode}.
|
||||
*
|
||||
* @param path The path of the node.
|
||||
*/
|
||||
protected Builder(String path) {
|
||||
this.path = path;
|
||||
this.comments = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a comment line to the node.
|
||||
*
|
||||
* @param comment The comment to add.
|
||||
* @return This builder.
|
||||
*/
|
||||
public B comment(@NotNull String comment) {
|
||||
if (!comment.isEmpty() && !comment.trim().startsWith("#")) {
|
||||
// Automatically add a comment prefix if the comment doesn't start with one.
|
||||
comment = "# " + comment;
|
||||
}
|
||||
this.comments.add(comment);
|
||||
return (B) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the node.
|
||||
*
|
||||
* @return The built node.
|
||||
*/
|
||||
public MVCommentedNode build() {
|
||||
return new MVCommentedNode(path, comments.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface Node {
|
||||
|
||||
/**
|
||||
* Gets the YAML path of the node.
|
||||
*
|
||||
* @return The YAML path of the node.
|
||||
*/
|
||||
@NotNull String getPath();
|
||||
}
|
@ -5,38 +5,38 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.github.townyadvanced.commentedconfiguration.setting.CommentedNode;
|
||||
import io.vavr.control.Option;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A collection of {@link CommentedNode}s, with mappings to nodes by name.
|
||||
*/
|
||||
public class NodeGroup implements Collection<CommentedNode> {
|
||||
private final Collection<CommentedNode> nodes;
|
||||
private final Map<String, CommentedNode> nodesMap;
|
||||
public class NodeGroup implements Collection<Node> {
|
||||
private final Collection<Node> nodes;
|
||||
private final Map<String, Node> nodesMap;
|
||||
|
||||
public NodeGroup() {
|
||||
this.nodes = new ArrayList<>();
|
||||
this.nodesMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public NodeGroup(Collection<CommentedNode> nodes) {
|
||||
public NodeGroup(Collection<Node> nodes) {
|
||||
this.nodes = nodes;
|
||||
this.nodesMap = new HashMap<>(nodes.size());
|
||||
nodes.forEach(this::addNodeIndex);
|
||||
}
|
||||
|
||||
private void addNodeIndex(CommentedNode node) {
|
||||
if (node instanceof EnhancedValueNode) {
|
||||
((EnhancedValueNode<?>) node).getName().ifPresent(name -> nodesMap.put(name, node));
|
||||
private void addNodeIndex(Node node) {
|
||||
if (node instanceof ValueNode) {
|
||||
((ValueNode<?>) node).getName().peek(name -> nodesMap.put(name, node));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeNodeIndex(CommentedNode node) {
|
||||
if (node instanceof EnhancedValueNode) {
|
||||
((EnhancedValueNode<?>) node).getName().ifPresent(nodesMap::remove);
|
||||
private void removeNodeIndex(Node node) {
|
||||
if (node instanceof ValueNode) {
|
||||
((ValueNode<?>) node).getName().peek(nodesMap::remove);
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,11 +52,22 @@ public class NodeGroup implements Collection<CommentedNode> {
|
||||
/**
|
||||
* Gets the node with the given name.
|
||||
*
|
||||
* @param name The name of the node to get.
|
||||
* @return The node with the given name, or {@link Optional#empty()} if no node with the given name exists.
|
||||
* @param name The name of the node to get.
|
||||
* @return The node with the given name, or {@link Option.None} if no node with the given name exists.
|
||||
*/
|
||||
public Optional<CommentedNode> findNode(String name) {
|
||||
return Optional.ofNullable(nodesMap.get(name));
|
||||
public Option<Node> findNode(String name) {
|
||||
return Option.of(nodesMap.get(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node with the given name.
|
||||
*
|
||||
* @param name The name of the node to get.
|
||||
* @param type The type of the node to get.
|
||||
* @return The node with the given name, or {@link Option.None} if no node with the given name exists.
|
||||
*/
|
||||
public <T extends Node> Option<T> findNode(String name, Class<T> type) {
|
||||
return Option.of(nodesMap.get(name)).map(node -> type.isAssignableFrom(node.getClass()) ? (T) node : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -76,7 +87,7 @@ public class NodeGroup implements Collection<CommentedNode> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<CommentedNode> iterator() {
|
||||
public Iterator<Node> iterator() {
|
||||
return nodes.iterator();
|
||||
}
|
||||
|
||||
@ -91,9 +102,9 @@ public class NodeGroup implements Collection<CommentedNode> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(CommentedNode commentedNode) {
|
||||
if (nodes.add(commentedNode)) {
|
||||
addNodeIndex(commentedNode);
|
||||
public boolean add(Node node) {
|
||||
if (nodes.add(node)) {
|
||||
addNodeIndex(node);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -102,7 +113,7 @@ public class NodeGroup implements Collection<CommentedNode> {
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
if (nodes.remove(o) && o instanceof CommentedNode) {
|
||||
removeNodeIndex((CommentedNode) o);
|
||||
removeNodeIndex((Node) o);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -114,7 +125,7 @@ public class NodeGroup implements Collection<CommentedNode> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends CommentedNode> collection) {
|
||||
public boolean addAll(@NotNull Collection<? extends Node> collection) {
|
||||
return nodes.addAll(collection);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
package com.onarandombox.MultiverseCore.configuration.node;
|
||||
|
||||
import io.vavr.control.Option;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface ValueNode<T> extends Node {
|
||||
|
||||
/**
|
||||
* Gets the name of this node. Used for identifying the node from user input.
|
||||
*
|
||||
* @return An {@link Option} containing the name of this node, or {@link Option.None} if the node has no name.
|
||||
*/
|
||||
@NotNull Option<String> getName();
|
||||
|
||||
/**
|
||||
* Gets the class type {@link T} of the node value.
|
||||
*
|
||||
* @return The class type of the node value.
|
||||
*/
|
||||
@NotNull Class<T> getType();
|
||||
|
||||
/**
|
||||
* Gets the default value with type {@link T} of the node.
|
||||
*
|
||||
* @return The default value of the node.
|
||||
*/
|
||||
@Nullable T getDefaultValue();
|
||||
|
||||
/**
|
||||
* Validates the value of this node.
|
||||
*
|
||||
* @param value The value to validate.
|
||||
* @return True if the value is valid, false otherwise.
|
||||
*/
|
||||
boolean validate(@Nullable T value);
|
||||
|
||||
/**
|
||||
* Called when the value of this node is set.
|
||||
*
|
||||
* @param oldValue The old value.
|
||||
* @param newValue The new value.
|
||||
*/
|
||||
void onSetValue(@Nullable T oldValue, @Nullable T newValue);
|
||||
}
|
@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
public enum MVCorei18n implements MessageKeyProvider {
|
||||
// config status
|
||||
CONFIG_SAVE_FAILED,
|
||||
CONFIG_NODE_NOTFOUND,
|
||||
|
||||
// check command
|
||||
CHECK_CHECKING,
|
||||
|
@ -1,5 +1,6 @@
|
||||
# configuration
|
||||
mv-core.config.save.failed=Unable to save Multiverse-Core config.yml. Your changes will be temporary!
|
||||
mv-core.config.node.notfound=Node not found in config: {node}
|
||||
|
||||
# /mv check
|
||||
mv-core.check.description=Checks if a player can teleport to a destination.
|
||||
@ -113,4 +114,3 @@ mv-core.unload.success=&aUnloaded world '{world}'!
|
||||
|
||||
# /mv usage
|
||||
mv-core.usage.description=Show Multiverse-Core command usage.
|
||||
|
||||
|
@ -59,14 +59,14 @@ class ConfigTest : TestWithMockBukkit() {
|
||||
|
||||
@Test
|
||||
fun `Getting existing config property with getProperty returns expected value`() {
|
||||
assertEquals(false, config.getProperty("enforce-access"))
|
||||
assertEquals("world", config.getProperty("first-spawn-location"))
|
||||
assertEquals(false, config.getProperty("enforce-access").get())
|
||||
assertEquals("world", config.getProperty("first-spawn-location").get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Getting non-existing config property with getProperty returns null`() {
|
||||
assertNull(config.getProperty("invalid-property"))
|
||||
assertNull(config.getProperty("version"))
|
||||
assertTrue(config.getProperty("invalid-property").isFailure)
|
||||
assertTrue(config.getProperty("version").isFailure)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -77,19 +77,19 @@ class ConfigTest : TestWithMockBukkit() {
|
||||
|
||||
@Test
|
||||
fun `Updating an existing config property with setProperty reflects the changes in getProperty`() {
|
||||
assertTrue(config.setProperty("enforce-access", true))
|
||||
assertEquals(true, config.getProperty("enforce-access"))
|
||||
assertTrue(config.setProperty("enforce-access", true).isSuccess)
|
||||
assertEquals(true, config.getProperty("enforce-access").get())
|
||||
|
||||
assertTrue(config.setProperty("first-spawn-location", "world2"))
|
||||
assertEquals("world2", config.getProperty("first-spawn-location"))
|
||||
assertTrue(config.setProperty("first-spawn-location", "world2").isSuccess)
|
||||
assertEquals("world2", config.getProperty("first-spawn-location").get())
|
||||
|
||||
assertTrue(config.setProperty("global-debug", 1))
|
||||
assertEquals(1, config.getProperty("global-debug"))
|
||||
assertTrue(config.setProperty("global-debug", 1).isSuccess)
|
||||
assertEquals(1, config.getProperty("global-debug").get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Updating a non-existing property with setProperty returns false`() {
|
||||
assertFalse(config.setProperty("invalid-property", false))
|
||||
assertFalse(config.setProperty("version", 1.1))
|
||||
assertTrue(config.setProperty("invalid-property", false).isFailure)
|
||||
assertTrue(config.setProperty("version", 1.1).isFailure)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user