Revert all the SongodaYamlConfig related commits
Revert "Ensures usage of UTF-8 in SongodaYamlConfig" This reverts commit339a4d6f6c
. Revert "Improve test coverage + stability of configuration.yaml/songoda classes" This reverts commitef6c37b80c
. Revert "Adds ConfigEntry#withDefaultValue for easier chaining" This reverts commit88e28689f7
. Revert "Code cleanup (rename e->ex in catch; better type for #withUpgradeStep)" This reverts commit7eff3c86ec
. Revert "Rename constant into upper case to match code conventions" This reverts commit4d194ed92b
. Revert "Fix typo in JavaDoc" This reverts commit0b2a253014
. Revert "Makes SongodaYamlConfig#cannotCreateBackupCopyExceptionPrefix static" This reverts commit8e91cc18eb
. Revert "Make unit tests in LocaleFileManagerTest deterministic" This reverts commit67a69e34e8
. Revert "Add unit test for SongodaYamlConfig persisting comments on key-upgrades" This reverts commitd710b2d2d5
. Revert "Improve temporary file deletion in YamlConfig and FileManager tests" This reverts commit02330b5ca7
. Revert "Adds hyphen before timestamp in file name, when creating backup YamlCfg" This reverts commitf8b3942de2
. Revert "Fix YamlConfiguration not dumping comments" This reverts commite7da328dc6
. Revert "Provisional first implementation of the new localization system" This reverts commitb168ad0738
. Revert "Fix error handling of SongodaYamlConfig#load(Reader)" This reverts commit163e4d9eaf
. Partially reverts "Adds some deprecation notices to configuration.editor classes" This partially reverts commiteea951ecc6
. Revert "Redo ConfigEntry abstraction" This reverts commit20b44327e0
. Revert "Migrate CustomizableGui from old Config to SongodaYamlConfig" This reverts commitd5ddde3e08
. Revert "Adds SongodaYamlConfig#getAsEntry(String) for convenience" This reverts commit20b7a353b8
. Revert "Add contract to `ConfigEntry#getString(String)` for non-null-argument" This reverts commit78b6039d39
. Revert "Adds getter to ConfigEntry for List<String>" This reverts commit3a09c19dbb
. Revert "Remove usage of Locale classes" This reverts commitda3c89450e
. Revert "Mark overwritten and empty config methods in SongodaPlugin as deprecated" This reverts commit73685b62dd
. Revert "Adjust log levels in SongodaYamlConfig" This reverts commit7ef00bb8f9
. Revert "Fix SongodaYamlConfig not creating parent directory when saving" This reverts commitb0f006aed0
. Revert "Fix SongodaYamlConfigTest leaving created backup files in tmp dir" This reverts commitc9a48387de
. Revert "Remove Config related methods in SongodaPlugin" This reverts commitfce5c5c6a1
. Revert "Introduce new SongodaYamlConfig and ConfigEntry classes" This reverts commiteb10b3f70a
. Revert "Fix YamlConfiguration dumping null values and empty tree nodes" This reverts commit02ab8d4bb2
. Revert "Fix `YamlConfiruration#getKeys("")` not returning root node keys" This reverts commit885cc9a87e
. Revert "Fix exception on loading empty file in YamlConfiguration" This reverts commit2683bc12c0
. Revert "Make YamlConfiguration insertion-sorted" This reverts commit2262652577
. Revert "Rename `IConfiguration#getOrDefault` to `#getOr`" This reverts commitf6e207cdda
. Revert "Adds Enum support to YamlConfiguration class (#41)" This reverts commit41bd5c633a
. Revert "Removes the default implementations for #save(File) and #load(File)" This reverts commit8f15df3601
. Revert "Replace Songoda's YAML Configuration wrapper with an own implementation" This reverts commit6d6fa7210a
.
This commit is contained in:
parent
748f10b77b
commit
c725ea69d6
|
@ -3,6 +3,7 @@ package com.songoda.core;
|
||||||
import com.songoda.core.commands.CommandManager;
|
import com.songoda.core.commands.CommandManager;
|
||||||
import com.songoda.core.compatibility.ClientVersion;
|
import com.songoda.core.compatibility.ClientVersion;
|
||||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||||
|
import com.songoda.core.core.LocaleModule;
|
||||||
import com.songoda.core.core.PluginInfo;
|
import com.songoda.core.core.PluginInfo;
|
||||||
import com.songoda.core.core.PluginInfoModule;
|
import com.songoda.core.core.PluginInfoModule;
|
||||||
import com.songoda.core.core.SongodaCoreCommand;
|
import com.songoda.core.core.SongodaCoreCommand;
|
||||||
|
@ -233,7 +234,7 @@ public class SongodaCore {
|
||||||
PluginInfo info = new PluginInfo(plugin, pluginID, icon, libraryVersion);
|
PluginInfo info = new PluginInfo(plugin, pluginID, icon, libraryVersion);
|
||||||
|
|
||||||
// don't forget to check for language pack updates ;)
|
// don't forget to check for language pack updates ;)
|
||||||
// info.addModule(new LocaleModule());
|
info.addModule(new LocaleModule());
|
||||||
registeredPlugins.add(info);
|
registeredPlugins.add(info);
|
||||||
tasks.add(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> update(info), 60L));
|
tasks.add(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> update(info), 60L));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
package com.songoda.core;
|
package com.songoda.core;
|
||||||
|
|
||||||
import com.songoda.core.configuration.songoda.SongodaYamlConfig;
|
import com.songoda.core.configuration.Config;
|
||||||
import com.songoda.core.database.DataManagerAbstract;
|
import com.songoda.core.database.DataManagerAbstract;
|
||||||
|
import com.songoda.core.locale.Locale;
|
||||||
import com.songoda.core.utils.Metrics;
|
import com.songoda.core.utils.Metrics;
|
||||||
import com.songoda.core.utils.SongodaAuth;
|
import com.songoda.core.utils.SongodaAuth;
|
||||||
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -21,7 +21,8 @@ import java.util.logging.Level;
|
||||||
* Must not have two instances of Metrics enabled!
|
* Must not have two instances of Metrics enabled!
|
||||||
*/
|
*/
|
||||||
public abstract class SongodaPlugin extends JavaPlugin {
|
public abstract class SongodaPlugin extends JavaPlugin {
|
||||||
// protected Locale locale;
|
protected Locale locale;
|
||||||
|
protected Config config = new Config(this);
|
||||||
protected long dataLoadDelay = 20L;
|
protected long dataLoadDelay = 20L;
|
||||||
|
|
||||||
private boolean emergencyStop = false;
|
private boolean emergencyStop = false;
|
||||||
|
@ -41,15 +42,37 @@ public abstract class SongodaPlugin extends JavaPlugin {
|
||||||
public abstract void onDataLoad();
|
public abstract void onDataLoad();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All the configuration files belonging to the plugin.<br>
|
* Called after reloadConfig() is called
|
||||||
* This might for example be used for the ingame config editor.<br>
|
|
||||||
* <br>
|
|
||||||
* Do not include *storage* files here or anything similar that does not intend external modification and access.<br>
|
|
||||||
* <br>
|
|
||||||
* Do not include language files if you are using the Core's localization system.
|
|
||||||
*/
|
*/
|
||||||
public abstract @NotNull List<SongodaYamlConfig> getConfigs();
|
public abstract @NotNull List<SongodaYamlConfig> getConfigs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any other plugin configuration files used by the plugin.
|
||||||
|
*
|
||||||
|
* @return a list of Configs that are used in addition to the main config.
|
||||||
|
*/
|
||||||
|
public abstract List<Config> getExtraConfig();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileConfiguration getConfig() {
|
||||||
|
return config.getFileConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config getCoreConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reloadConfig() {
|
||||||
|
config.load();
|
||||||
|
onConfigReload();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveConfig() {
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void onLoad() {
|
public final void onLoad() {
|
||||||
try {
|
try {
|
||||||
|
@ -103,7 +126,7 @@ public abstract class SongodaPlugin extends JavaPlugin {
|
||||||
ChatColor.GREEN, "Enabling", ChatColor.GRAY));
|
ChatColor.GREEN, "Enabling", ChatColor.GRAY));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// this.locale = Locale.loadDefaultLocale(this, "en_US");
|
this.locale = Locale.loadDefaultLocale(this, "en_US");
|
||||||
|
|
||||||
// plugin setup
|
// plugin setup
|
||||||
onPluginEnable();
|
onPluginEnable();
|
||||||
|
@ -157,32 +180,32 @@ public abstract class SongodaPlugin extends JavaPlugin {
|
||||||
console.sendMessage(" "); // blank line to separate chatter
|
console.sendMessage(" "); // blank line to separate chatter
|
||||||
}
|
}
|
||||||
|
|
||||||
// public Locale getLocale() {
|
public Locale getLocale() {
|
||||||
// return this.locale;
|
return this.locale;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Set the plugin's locale to a specific language
|
* Set the plugin's locale to a specific language
|
||||||
// *
|
*
|
||||||
// * @param localeName locale to use, eg "en_US"
|
* @param localeName locale to use, eg "en_US"
|
||||||
// * @param reload optionally reload the loaded locale if the locale didn't
|
* @param reload optionally reload the loaded locale if the locale didn't
|
||||||
// * change
|
* change
|
||||||
// *
|
*
|
||||||
// * @return true if the locale exists and was loaded successfully
|
* @return true if the locale exists and was loaded successfully
|
||||||
// */
|
*/
|
||||||
// public boolean setLocale(String localeName, boolean reload) {
|
public boolean setLocale(String localeName, boolean reload) {
|
||||||
// if (this.locale != null && this.locale.getName().equals(localeName)) {
|
if (this.locale != null && this.locale.getName().equals(localeName)) {
|
||||||
// return !reload || this.locale.reloadMessages();
|
return !reload || this.locale.reloadMessages();
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// Locale l = Locale.loadLocale(this, localeName);
|
Locale l = Locale.loadLocale(this, localeName);
|
||||||
// if (l != null) {
|
if (l != null) {
|
||||||
// this.locale = l;
|
this.locale = l;
|
||||||
// return true;
|
return true;
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// return false;
|
return false;
|
||||||
// }
|
}
|
||||||
|
|
||||||
protected void shutdownDataManager(DataManagerAbstract dataManager) {
|
protected void shutdownDataManager(DataManagerAbstract dataManager) {
|
||||||
// 3 minutes is overkill, but we just want to make sure
|
// 3 minutes is overkill, but we just want to make sure
|
||||||
|
@ -243,29 +266,4 @@ public abstract class SongodaPlugin extends JavaPlugin {
|
||||||
|
|
||||||
emergencyStop();
|
emergencyStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Use {@link SongodaYamlConfig} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
@Override
|
|
||||||
public @NotNull FileConfiguration getConfig() {
|
|
||||||
return super.getConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use {@link SongodaYamlConfig} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
@Override
|
|
||||||
public void reloadConfig() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use {@link SongodaYamlConfig} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
@Override
|
|
||||||
public void saveConfig() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
package com.songoda.core.configuration;
|
||||||
|
|
||||||
|
import com.songoda.core.configuration.ConfigFormattingRules.CommentStyle;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A comment for a configuration key
|
||||||
|
*/
|
||||||
|
public class Comment {
|
||||||
|
final List<String> lines = new ArrayList<>();
|
||||||
|
CommentStyle commentStyle = null;
|
||||||
|
|
||||||
|
public Comment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comment(String... lines) {
|
||||||
|
this(null, Arrays.asList(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comment(List<String> lines) {
|
||||||
|
this(null, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comment(CommentStyle commentStyle, String... lines) {
|
||||||
|
this(commentStyle, Arrays.asList(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comment(CommentStyle commentStyle, List<String> lines) {
|
||||||
|
this.commentStyle = commentStyle;
|
||||||
|
|
||||||
|
if (lines != null) {
|
||||||
|
lines.forEach(s -> this.lines.addAll(Arrays.asList(s.split("\n"))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommentStyle getCommentStyle() {
|
||||||
|
return commentStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCommentStyle(CommentStyle commentStyle) {
|
||||||
|
this.commentStyle = commentStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getLines() {
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return lines.isEmpty() ? "" : String.join("\n", lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Comment loadComment(List<String> lines) {
|
||||||
|
CommentStyle style = ConfigFormattingRules.parseStyle(lines);
|
||||||
|
|
||||||
|
int linePad = (style.drawBorder ? 1 : 0) + (style.drawSpace ? 1 : 0);
|
||||||
|
int prefix = style.commentPrefix.length();
|
||||||
|
int suffix = style.commentSuffix.length();
|
||||||
|
|
||||||
|
return new Comment(style, lines.subList(linePad, lines.size() - linePad).stream().map(s -> s.substring(prefix, s.length() - suffix).trim()).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeComment(Writer output, int offset, CommentStyle defaultStyle) throws IOException {
|
||||||
|
CommentStyle style = commentStyle != null ? commentStyle : defaultStyle;
|
||||||
|
int minSpacing = 0, borderSpacing = 0;
|
||||||
|
|
||||||
|
// first draw the top of the comment
|
||||||
|
if (style.drawBorder) {
|
||||||
|
// grab the longest line in the list of lines
|
||||||
|
minSpacing = lines.stream().max(Comparator.comparingInt(String::length)).orElse("").length();
|
||||||
|
borderSpacing = minSpacing + style.commentPrefix.length() + style.commentSuffix.length();
|
||||||
|
|
||||||
|
// draw the first line
|
||||||
|
output.write((new String(new char[offset])).replace('\0', ' ') + (new String(new char[borderSpacing + 2])).replace('\0', '#') + "\n");
|
||||||
|
|
||||||
|
if (style.drawSpace) {
|
||||||
|
output.write((new String(new char[offset])).replace('\0', ' ')
|
||||||
|
+ "#" + style.spacePrefixTop
|
||||||
|
+ (new String(new char[borderSpacing - style.spacePrefixTop.length() - style.spaceSuffixTop.length()])).replace('\0', style.spaceCharTop)
|
||||||
|
+ style.spaceSuffixTop + "#\n");
|
||||||
|
}
|
||||||
|
} else if (style.drawSpace) {
|
||||||
|
output.write((new String(new char[offset])).replace('\0', ' ') + "#\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// then the actual comment lines
|
||||||
|
for (String line : lines) {
|
||||||
|
// todo? should we auto-wrap comment lines that are longer than 80 characters?
|
||||||
|
output.write((new String(new char[offset])).replace('\0', ' ') + "#" + style.commentPrefix
|
||||||
|
+ (minSpacing == 0 ? line : line + (new String(new char[minSpacing - line.length()])).replace('\0', ' ')) + style.commentSuffix + (style.drawBorder ? "#\n" : "\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// now draw the bottom of the comment border
|
||||||
|
if (style.drawBorder) {
|
||||||
|
if (style.drawSpace) {
|
||||||
|
output.write((new String(new char[offset])).replace('\0', ' ')
|
||||||
|
+ "#" + style.spacePrefixBottom
|
||||||
|
+ (new String(new char[borderSpacing - style.spacePrefixBottom.length() - style.spaceSuffixBottom.length()])).replace('\0', style.spaceCharBottom)
|
||||||
|
+ style.spaceSuffixBottom + "#\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
output.write((new String(new char[offset])).replace('\0', ' ') + (new String(new char[borderSpacing + 2])).replace('\0', '#') + "\n");
|
||||||
|
} else if (style.drawSpace) {
|
||||||
|
output.write((new String(new char[offset])).replace('\0', ' ') + "#\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,793 @@
|
||||||
|
package com.songoda.core.configuration;
|
||||||
|
|
||||||
|
import com.songoda.core.utils.TextUtils;
|
||||||
|
import org.apache.commons.lang.Validate;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
|
import org.bukkit.configuration.file.YamlConstructor;
|
||||||
|
import org.bukkit.configuration.file.YamlRepresenter;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.yaml.snakeyaml.DumperOptions;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
import org.yaml.snakeyaml.error.YAMLException;
|
||||||
|
import org.yaml.snakeyaml.representer.Representer;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration settings for a plugin
|
||||||
|
*/
|
||||||
|
public class Config extends ConfigSection {
|
||||||
|
/*
|
||||||
|
Serialization notes:
|
||||||
|
// implements ConfigurationSerializable:
|
||||||
|
//public Map<String, Object> serialize();
|
||||||
|
|
||||||
|
// Class must contain one of:
|
||||||
|
// public static Object deserialize(@NotNull Map<String, ?> args);
|
||||||
|
// public static valueOf(Map<String, ?> args);
|
||||||
|
// public new (Map<String, ?> args)
|
||||||
|
*/
|
||||||
|
protected static final String BLANK_CONFIG = "{}\n";
|
||||||
|
|
||||||
|
protected File file;
|
||||||
|
protected final ConfigFileConfigurationAdapter config = new ConfigFileConfigurationAdapter(this);
|
||||||
|
protected Comment headerComment = null;
|
||||||
|
protected Comment footerComment = null;
|
||||||
|
final String dirName, fileName;
|
||||||
|
final Plugin plugin;
|
||||||
|
final DumperOptions yamlOptions = new DumperOptions();
|
||||||
|
final Representer yamlRepresenter = new YamlRepresenter();
|
||||||
|
final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions);
|
||||||
|
Charset defaultCharset = StandardCharsets.UTF_8;
|
||||||
|
SaveTask saveTask;
|
||||||
|
Timer autosaveTimer;
|
||||||
|
|
||||||
|
////////////// Config settings ////////////////
|
||||||
|
/**
|
||||||
|
* save file whenever a change is made
|
||||||
|
*/
|
||||||
|
boolean autosave = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* time in seconds to start a save after a change is made
|
||||||
|
*/
|
||||||
|
int autosaveInterval = 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove nodes not defined in defaults
|
||||||
|
*/
|
||||||
|
boolean autoremove = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load comments when loading the file
|
||||||
|
*/
|
||||||
|
boolean loadComments = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default comment applied to config nodes
|
||||||
|
*/
|
||||||
|
ConfigFormattingRules.CommentStyle defaultNodeCommentFormat = ConfigFormattingRules.CommentStyle.SIMPLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default comment applied to section nodes
|
||||||
|
*/
|
||||||
|
ConfigFormattingRules.CommentStyle defaultSectionCommentFormat = ConfigFormattingRules.CommentStyle.SPACED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra lines to put between root nodes
|
||||||
|
*/
|
||||||
|
int rootNodeSpacing = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra lines to put in front of comments. <br>
|
||||||
|
* This is separate from rootNodeSpacing, if applicable.
|
||||||
|
*/
|
||||||
|
int commentSpacing = 1;
|
||||||
|
|
||||||
|
public Config() {
|
||||||
|
this.plugin = null;
|
||||||
|
this.file = null;
|
||||||
|
|
||||||
|
dirName = null;
|
||||||
|
fileName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config(@NotNull File file) {
|
||||||
|
this.plugin = null;
|
||||||
|
this.file = file.getAbsoluteFile();
|
||||||
|
|
||||||
|
dirName = null;
|
||||||
|
fileName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config(@NotNull Plugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
dirName = null;
|
||||||
|
fileName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config(@NotNull Plugin plugin, @NotNull String file) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
dirName = null;
|
||||||
|
fileName = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config(@NotNull Plugin plugin, @Nullable String directory, @NotNull String file) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
dirName = directory;
|
||||||
|
fileName = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigFileConfigurationAdapter getFileConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public File getFile() {
|
||||||
|
if (file == null) {
|
||||||
|
if (dirName != null) {
|
||||||
|
this.file = new File(plugin.getDataFolder() + dirName, fileName != null ? fileName : "config.yml");
|
||||||
|
} else {
|
||||||
|
this.file = new File(plugin.getDataFolder(), fileName != null ? fileName : "config.yml");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Charset getDefaultCharset() {
|
||||||
|
return defaultCharset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Charset that will be used to save this config
|
||||||
|
*
|
||||||
|
* @param defaultCharset Charset to use
|
||||||
|
*
|
||||||
|
* @return this class
|
||||||
|
*/
|
||||||
|
public Config setDefaultCharset(Charset defaultCharset) {
|
||||||
|
this.defaultCharset = defaultCharset;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default charset to use UTF-16
|
||||||
|
*
|
||||||
|
* @return this class
|
||||||
|
*/
|
||||||
|
public Config setUseUTF16() {
|
||||||
|
this.defaultCharset = StandardCharsets.UTF_16;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getLoadComments() {
|
||||||
|
return loadComments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should comments from the config file be loaded when loading?
|
||||||
|
*
|
||||||
|
* @param loadComments set to false if you do not want to preserve node comments
|
||||||
|
*/
|
||||||
|
public void setLoadComments(boolean loadComments) {
|
||||||
|
this.loadComments = loadComments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getAutosave() {
|
||||||
|
return autosave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the configuration automatically save whenever it's been changed? <br>
|
||||||
|
* All saves are done asynchronously, so this should not impact server performance.
|
||||||
|
*
|
||||||
|
* @param autosave set to true if autosaving is enabled.
|
||||||
|
*
|
||||||
|
* @return this class
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public Config setAutosave(boolean autosave) {
|
||||||
|
this.autosave = autosave;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAutosaveInterval() {
|
||||||
|
return autosaveInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If autosave is enabled, this is the delay between a change and when the save is started. <br>
|
||||||
|
* If the configuration is changed within this period, the timer is not reset.
|
||||||
|
*
|
||||||
|
* @param autosaveInterval time in seconds
|
||||||
|
*
|
||||||
|
* @return this class
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public Config setAutosaveInterval(int autosaveInterval) {
|
||||||
|
this.autosaveInterval = autosaveInterval;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getAutoremove() {
|
||||||
|
return autoremove;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This setting is used to prevent users to from adding extraneous settings
|
||||||
|
* to the config and to remove deprecated settings. <br>
|
||||||
|
* If this is enabled, the config will delete any nodes that are not defined
|
||||||
|
* as a default setting.
|
||||||
|
*
|
||||||
|
* @param autoremove Remove settings that don't exist as defaults
|
||||||
|
*
|
||||||
|
* @return this class
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public Config setAutoremove(boolean autoremove) {
|
||||||
|
this.autoremove = autoremove;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default comment applied to config nodes
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public ConfigFormattingRules.CommentStyle getDefaultNodeCommentFormat() {
|
||||||
|
return defaultNodeCommentFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default comment applied to config nodes
|
||||||
|
*
|
||||||
|
* @return this config
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public Config setDefaultNodeCommentFormat(@Nullable ConfigFormattingRules.CommentStyle defaultNodeCommentFormat) {
|
||||||
|
this.defaultNodeCommentFormat = defaultNodeCommentFormat;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default comment applied to section nodes
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public ConfigFormattingRules.CommentStyle getDefaultSectionCommentFormat() {
|
||||||
|
return defaultSectionCommentFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default comment applied to section nodes
|
||||||
|
*
|
||||||
|
* @return this config
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public Config setDefaultSectionCommentFormat(@Nullable ConfigFormattingRules.CommentStyle defaultSectionCommentFormat) {
|
||||||
|
this.defaultSectionCommentFormat = defaultSectionCommentFormat;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra lines to put between root nodes
|
||||||
|
*/
|
||||||
|
public int getRootNodeSpacing() {
|
||||||
|
return rootNodeSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra lines to put between root nodes
|
||||||
|
*
|
||||||
|
* @return this config
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public Config setRootNodeSpacing(int rootNodeSpacing) {
|
||||||
|
this.rootNodeSpacing = rootNodeSpacing;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra lines to put in front of comments. <br>
|
||||||
|
* This is separate from rootNodeSpacing, if applicable.
|
||||||
|
*/
|
||||||
|
public int getCommentSpacing() {
|
||||||
|
return commentSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra lines to put in front of comments. <br>
|
||||||
|
* This is separate from rootNodeSpacing, if applicable.
|
||||||
|
*
|
||||||
|
* @return this config
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public Config setCommentSpacing(int commentSpacing) {
|
||||||
|
this.commentSpacing = commentSpacing;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Config setHeader(@NotNull String... description) {
|
||||||
|
if (description.length == 0) {
|
||||||
|
headerComment = null;
|
||||||
|
} else {
|
||||||
|
headerComment = new Comment(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Config setHeader(@Nullable ConfigFormattingRules.CommentStyle commentStyle, @NotNull String... description) {
|
||||||
|
if (description.length == 0) {
|
||||||
|
headerComment = null;
|
||||||
|
} else {
|
||||||
|
headerComment = new Comment(commentStyle, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Config setHeader(@Nullable List<String> description) {
|
||||||
|
if (description == null || description.isEmpty()) {
|
||||||
|
headerComment = null;
|
||||||
|
} else {
|
||||||
|
headerComment = new Comment(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Config setHeader(@Nullable ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> description) {
|
||||||
|
if (description == null || description.isEmpty()) {
|
||||||
|
headerComment = null;
|
||||||
|
} else {
|
||||||
|
headerComment = new Comment(commentStyle, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public List<String> getHeader() {
|
||||||
|
if (headerComment != null) {
|
||||||
|
return headerComment.getLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config clearConfig(boolean clearDefaults) {
|
||||||
|
root.values.clear();
|
||||||
|
root.configComments.clear();
|
||||||
|
|
||||||
|
if (clearDefaults) {
|
||||||
|
root.defaultComments.clear();
|
||||||
|
root.defaults.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config clearDefaults() {
|
||||||
|
root.defaultComments.clear();
|
||||||
|
root.defaults.clear();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean load() {
|
||||||
|
return load(getFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean load(@NotNull File file) {
|
||||||
|
Validate.notNull(file, "File cannot be null");
|
||||||
|
if (file.exists()) {
|
||||||
|
try (BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file))) {
|
||||||
|
Charset charset = TextUtils.detectCharset(stream, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
// upgrade charset if file was saved in a more complex format
|
||||||
|
if (charset == StandardCharsets.UTF_16BE || charset == StandardCharsets.UTF_16LE) {
|
||||||
|
defaultCharset = StandardCharsets.UTF_16;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.load(new InputStreamReader(stream, charset));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (IOException | InvalidConfigurationException ex) {
|
||||||
|
(plugin != null ? plugin.getLogger() : Bukkit.getLogger()).log(Level.SEVERE, "Failed to load config file: " + file.getName(), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(@NotNull Reader reader) throws IOException, InvalidConfigurationException {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
try (BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader)) {
|
||||||
|
String line;
|
||||||
|
boolean firstLine = true;
|
||||||
|
while ((line = input.readLine()) != null) {
|
||||||
|
if (firstLine) {
|
||||||
|
line = line.replaceAll("[\uFEFF\uFFFE\u200B]", ""); // clear BOM markers
|
||||||
|
firstLine = false;
|
||||||
|
}
|
||||||
|
builder.append(line).append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadFromString(builder.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadFromString(@NotNull String contents) throws InvalidConfigurationException {
|
||||||
|
Map<?, ?> input;
|
||||||
|
|
||||||
|
try {
|
||||||
|
input = this.yaml.load(contents);
|
||||||
|
} catch (YAMLException e2) {
|
||||||
|
throw new InvalidConfigurationException(e2);
|
||||||
|
} catch (ClassCastException e3) {
|
||||||
|
throw new InvalidConfigurationException("Top level is not a Map.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input != null) {
|
||||||
|
if (loadComments) {
|
||||||
|
this.parseComments(contents, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.convertMapsToSections(input, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void convertMapsToSections(@NotNull Map<?, ?> input, @NotNull ConfigSection section) {
|
||||||
|
// TODO: make this non-recursive
|
||||||
|
for (Map.Entry<?, ?> entry : input.entrySet()) {
|
||||||
|
String key = entry.getKey().toString();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
|
||||||
|
if (value instanceof Map) {
|
||||||
|
this.convertMapsToSections((Map<?, ?>) value, section.createSection(key));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void parseComments(@NotNull String contents, @NotNull Map<?, ?> input) {
|
||||||
|
// if starts with a comment, load all nonbreaking comments as a header
|
||||||
|
// then load all comments and assign to the next valid node loaded
|
||||||
|
// (Only load comments that are on their own line)
|
||||||
|
|
||||||
|
BufferedReader in = new BufferedReader(new StringReader(contents));
|
||||||
|
String line;
|
||||||
|
boolean insideScalar = false;
|
||||||
|
boolean firstNode = true;
|
||||||
|
int index = 0;
|
||||||
|
LinkedList<String> currentPath = new LinkedList<>();
|
||||||
|
ArrayList<String> commentBlock = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
while ((line = in.readLine()) != null) {
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
if (firstNode && !commentBlock.isEmpty()) {
|
||||||
|
// header comment
|
||||||
|
firstNode = false;
|
||||||
|
headerComment = Comment.loadComment(commentBlock);
|
||||||
|
commentBlock.clear();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (line.trim().startsWith("#")) {
|
||||||
|
// only load full-line comments
|
||||||
|
commentBlock.add(line.trim());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if this is a line that we can process
|
||||||
|
int lineOffset = getOffset(line);
|
||||||
|
insideScalar &= lineOffset <= index;
|
||||||
|
Matcher m;
|
||||||
|
if (!insideScalar && (m = yamlNode.matcher(line)).find()) {
|
||||||
|
// we found a config node! ^.^
|
||||||
|
// check to see what the full path is
|
||||||
|
int depth = (m.group(1).length() / indentation);
|
||||||
|
while (depth < currentPath.size()) {
|
||||||
|
currentPath.removeLast();
|
||||||
|
}
|
||||||
|
currentPath.add(m.group(2));
|
||||||
|
|
||||||
|
// do we have a comment for this node?
|
||||||
|
if (!commentBlock.isEmpty()) {
|
||||||
|
String path = currentPath.stream().collect(Collectors.joining(String.valueOf(pathChar)));
|
||||||
|
Comment comment = Comment.loadComment(commentBlock);
|
||||||
|
commentBlock.clear();
|
||||||
|
setComment(path, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
firstNode = false; // we're no longer on the first node
|
||||||
|
|
||||||
|
// ignore scalars
|
||||||
|
index = lineOffset;
|
||||||
|
if (m.group(3).trim().equals("|") || m.group(3).trim().equals(">")) {
|
||||||
|
insideScalar = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!commentBlock.isEmpty()) {
|
||||||
|
footerComment = Comment.loadComment(commentBlock);
|
||||||
|
commentBlock.clear();
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.getLogger(Config.class.getName()).log(Level.SEVERE, "Error parsing config comment", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteNonDefaultSettings() {
|
||||||
|
// Delete old config values (thread-safe)
|
||||||
|
List<String> defaultKeys = Arrays.asList(defaults.keySet().toArray(new String[0]));
|
||||||
|
|
||||||
|
for (String key : values.keySet().toArray(new String[0])) {
|
||||||
|
if (!defaultKeys.contains(key)) {
|
||||||
|
values.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onChange() {
|
||||||
|
if (autosave) {
|
||||||
|
delaySave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delaySave() {
|
||||||
|
// save async even if no plugin or if plugin disabled
|
||||||
|
if (saveTask == null && (changed || hasNewDefaults())) {
|
||||||
|
autosaveTimer = new Timer((plugin != null ? plugin.getName() + "-ConfigSave-" : "ConfigSave-") + getFile().getName());
|
||||||
|
autosaveTimer.schedule(saveTask = new SaveTask(), autosaveInterval * 1000L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean saveChanges() {
|
||||||
|
boolean saved = true;
|
||||||
|
|
||||||
|
if (changed || hasNewDefaults()) {
|
||||||
|
saved = save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saveTask != null) {
|
||||||
|
//Close Threads
|
||||||
|
saveTask.cancel();
|
||||||
|
autosaveTimer.cancel();
|
||||||
|
saveTask = null;
|
||||||
|
autosaveTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasNewDefaults() {
|
||||||
|
if (file != null && !file.exists()) return true;
|
||||||
|
|
||||||
|
for (String def : defaults.keySet()) {
|
||||||
|
if (!values.containsKey(def)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean save() {
|
||||||
|
if (saveTask != null) {
|
||||||
|
//Close Threads
|
||||||
|
saveTask.cancel();
|
||||||
|
autosaveTimer.cancel();
|
||||||
|
saveTask = null;
|
||||||
|
autosaveTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return save(getFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean save(@NotNull String file) {
|
||||||
|
Validate.notNull(file, "File cannot be null");
|
||||||
|
return this.save(new File(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean save(@NotNull File file) {
|
||||||
|
Validate.notNull(file, "File cannot be null");
|
||||||
|
|
||||||
|
if (file.getParentFile() != null && !file.getParentFile().exists()) {
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
String data = this.saveToString();
|
||||||
|
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), defaultCharset)) {
|
||||||
|
writer.write(data);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String saveToString() {
|
||||||
|
try {
|
||||||
|
if (autoremove) {
|
||||||
|
deleteNonDefaultSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
yamlOptions.setIndent(indentation);
|
||||||
|
yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||||
|
yamlOptions.setSplitLines(false);
|
||||||
|
yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||||
|
|
||||||
|
StringWriter str = new StringWriter();
|
||||||
|
|
||||||
|
if (headerComment != null) {
|
||||||
|
headerComment.writeComment(str, 0, ConfigFormattingRules.CommentStyle.BLOCKED);
|
||||||
|
str.write("\n"); // add one space after the header
|
||||||
|
}
|
||||||
|
|
||||||
|
String dump = yaml.dump(this.getValues(false));
|
||||||
|
if (!dump.equals(BLANK_CONFIG)) {
|
||||||
|
writeComments(dump, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (footerComment != null) {
|
||||||
|
str.write("\n");
|
||||||
|
footerComment.writeComment(str, 0, ConfigFormattingRules.CommentStyle.BLOCKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.toString();
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
Logger.getLogger(Config.class.getName()).log(Level.SEVERE, "Error saving config", ex);
|
||||||
|
delaySave();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Pattern yamlNode = Pattern.compile("^( *)([^:{}\\[\\],&*#?|\\-<>=!%@`]+):(.*)$");
|
||||||
|
|
||||||
|
protected void writeComments(String data, Writer out) throws IOException {
|
||||||
|
// line-by-line apply line spacing formatting and comments per-node
|
||||||
|
BufferedReader in = new BufferedReader(new StringReader(data));
|
||||||
|
|
||||||
|
String line;
|
||||||
|
boolean insideScalar = false;
|
||||||
|
boolean firstNode = true;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
LinkedList<String> currentPath = new LinkedList<>();
|
||||||
|
while ((line = in.readLine()) != null) {
|
||||||
|
// ignore comments and empty lines (there shouldn't be any, but just in case)
|
||||||
|
if (line.trim().startsWith("#") || line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if this is a line that we can process
|
||||||
|
int lineOffset = getOffset(line);
|
||||||
|
insideScalar &= lineOffset <= index;
|
||||||
|
Matcher m;
|
||||||
|
if (!insideScalar && (m = yamlNode.matcher(line)).find()) {
|
||||||
|
// we found a config node! ^.^
|
||||||
|
// check to see what the full path is
|
||||||
|
int depth = (m.group(1).length() / indentation);
|
||||||
|
while (depth < currentPath.size()) {
|
||||||
|
currentPath.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPath.add(m.group(2));
|
||||||
|
String path = currentPath.stream().collect(Collectors.joining(String.valueOf(pathChar)));
|
||||||
|
|
||||||
|
// if this is a root-level node, apply extra spacing if we aren't the first node
|
||||||
|
if (!firstNode && depth == 0 && rootNodeSpacing > 0) {
|
||||||
|
out.write((new String(new char[rootNodeSpacing])).replace("\0", "\n")); // yes it's silly, but it works :>
|
||||||
|
}
|
||||||
|
firstNode = false; // we're no longer on the first node
|
||||||
|
|
||||||
|
// insert the relavant comment
|
||||||
|
Comment comment = getComment(path);
|
||||||
|
if (comment != null) {
|
||||||
|
// add spacing between previous nodes and comments
|
||||||
|
if (depth != 0) {
|
||||||
|
out.write((new String(new char[commentSpacing])).replace("\0", "\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatting style for this node
|
||||||
|
ConfigFormattingRules.CommentStyle style = comment.getCommentStyle();
|
||||||
|
if (style == null) {
|
||||||
|
// check to see what type of node this is
|
||||||
|
if (!m.group(3).trim().isEmpty()) {
|
||||||
|
// setting node
|
||||||
|
style = defaultNodeCommentFormat;
|
||||||
|
} else {
|
||||||
|
// probably a section? (need to peek ahead to check if this is a list)
|
||||||
|
in.mark(1000);
|
||||||
|
String nextLine = in.readLine().trim();
|
||||||
|
in.reset();
|
||||||
|
if (nextLine.startsWith("-")) {
|
||||||
|
// not a section :P
|
||||||
|
style = defaultNodeCommentFormat;
|
||||||
|
} else {
|
||||||
|
style = defaultSectionCommentFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write it down!
|
||||||
|
comment.writeComment(out, lineOffset, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore scalars
|
||||||
|
index = lineOffset;
|
||||||
|
if (m.group(3).trim().equals("|") || m.group(3).trim().equals(">")) {
|
||||||
|
insideScalar = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write(line);
|
||||||
|
out.write("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int getOffset(String s) {
|
||||||
|
char[] chars = s.toCharArray();
|
||||||
|
for (int i = 0; i < chars.length; ++i) {
|
||||||
|
if (chars[i] != ' ') {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SaveTask extends TimerTask {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
saveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,189 +0,0 @@
|
||||||
package com.songoda.core.configuration;
|
|
||||||
|
|
||||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
|
||||||
import com.songoda.core.utils.Pair;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public interface ConfigEntry {
|
|
||||||
@NotNull String getKey();
|
|
||||||
|
|
||||||
@NotNull IConfiguration getConfig();
|
|
||||||
|
|
||||||
default boolean has() {
|
|
||||||
return getConfig().has(getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(@Nullable Object value);
|
|
||||||
|
|
||||||
default @Nullable Object get() {
|
|
||||||
return getConfig().get(getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable Object getOr(@Nullable Object fallbackValue) {
|
|
||||||
return getConfig().getOr(getKey(), fallbackValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable Object getDefaultValue();
|
|
||||||
|
|
||||||
void setDefaultValue(@Nullable Object defaultValue);
|
|
||||||
|
|
||||||
@Contract("_ -> this")
|
|
||||||
ConfigEntry withDefaultValue(@Nullable Object defaultValue);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see #withComment(Supplier)
|
|
||||||
*/
|
|
||||||
@Contract("_ -> this")
|
|
||||||
default ConfigEntry withComment(String comment) {
|
|
||||||
return this.withComment(() -> comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see NodeCommentable#setNodeComment(String, Supplier)
|
|
||||||
*/
|
|
||||||
@Contract("_ -> this")
|
|
||||||
ConfigEntry withComment(Supplier<String> comment);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return <code><configVersion, Pair<keyInGivenVersion, valueConverter>></code>
|
|
||||||
*/
|
|
||||||
@Nullable Map<Integer, Pair<@Nullable String, @Nullable Function<@Nullable Object, @Nullable Object>>> getUpgradeSteps();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see #withUpgradeStep(int, String, Function)
|
|
||||||
*/
|
|
||||||
@Contract("_, _ -> this")
|
|
||||||
default ConfigEntry withUpgradeStep(int version, @NotNull String keyInGivenVersion) {
|
|
||||||
return withUpgradeStep(version, keyInGivenVersion, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param version The version to upgrade from (e.g. 1 for the upgrade from 1 to 2)
|
|
||||||
* @param keyInGivenVersion The old key in the given version or null if it didn't change
|
|
||||||
* @param valueConverter A function that converts the old version's value to a new one, or null if it didn't change
|
|
||||||
*/
|
|
||||||
@Contract("_, null, null -> fail; _, _, _ -> this")
|
|
||||||
ConfigEntry withUpgradeStep(int version, @Nullable String keyInGivenVersion, @Nullable Function<@Nullable Object, @Nullable Object> valueConverter);
|
|
||||||
|
|
||||||
default @Nullable String getString() {
|
|
||||||
return getStringOr(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("!null -> !null")
|
|
||||||
default @Nullable String getStringOr(String fallbackValue) {
|
|
||||||
Object value = get();
|
|
||||||
|
|
||||||
return value == null ? fallbackValue : value.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see #getIntOr(int)
|
|
||||||
*/
|
|
||||||
default int getInt() {
|
|
||||||
return getIntOr(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the values parsed as an integer.<br>
|
|
||||||
* If it is a floating point number, it will be rounded down.
|
|
||||||
*
|
|
||||||
* @see Double#valueOf(String)
|
|
||||||
*/
|
|
||||||
default int getIntOr(int fallbackValue) {
|
|
||||||
String value = getString();
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
return fallbackValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Double.valueOf(value).intValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see #getDoubleOr(double)
|
|
||||||
*/
|
|
||||||
default double getDouble() {
|
|
||||||
return getDoubleOr(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the values parsed as a double.
|
|
||||||
*
|
|
||||||
* @see Double#parseDouble(String)
|
|
||||||
*/
|
|
||||||
default double getDoubleOr(double fallbackValue) {
|
|
||||||
String value = getString();
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
return fallbackValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Double.parseDouble(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see #getBooleanOr(boolean)
|
|
||||||
*/
|
|
||||||
default boolean getBoolean() {
|
|
||||||
return getBooleanOr(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the values parsed as a boolean.
|
|
||||||
*
|
|
||||||
* @see Boolean#parseBoolean(String)
|
|
||||||
*/
|
|
||||||
default boolean getBooleanOr(boolean fallbackValue) {
|
|
||||||
String value = getString();
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
return fallbackValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Boolean.parseBoolean(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable List<String> getStringList() {
|
|
||||||
return getStringListOr(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("!null -> !null")
|
|
||||||
default @Nullable List<String> getStringListOr(List<String> fallbackValue) {
|
|
||||||
Object value = get();
|
|
||||||
|
|
||||||
if (value instanceof List) {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (List<String>) value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fallbackValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see #getMaterialOr(CompatibleMaterial)
|
|
||||||
*/
|
|
||||||
default CompatibleMaterial getMaterial() {
|
|
||||||
return getMaterialOr(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see CompatibleMaterial#getMaterial(String)
|
|
||||||
*/
|
|
||||||
@Contract("!null -> !null")
|
|
||||||
default @Nullable CompatibleMaterial getMaterialOr(@Nullable CompatibleMaterial defaultValue) {
|
|
||||||
String value = getString();
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CompatibleMaterial.getMaterial(value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
package com.songoda.core.configuration;
|
||||||
|
|
||||||
|
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||||
|
import org.bukkit.configuration.Configuration;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ConfigFileConfigurationAdapter extends FileConfiguration {
|
||||||
|
final Config config;
|
||||||
|
|
||||||
|
public ConfigFileConfigurationAdapter(Config config) {
|
||||||
|
super(config);
|
||||||
|
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config getCoreConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String saveToString() {
|
||||||
|
return config.saveToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadFromString(String string) throws InvalidConfigurationException {
|
||||||
|
config.loadFromString(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String buildHeader() {
|
||||||
|
return "#" + String.join("\n#", config.getHeader());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigOptionsAdapter options() {
|
||||||
|
return new ConfigOptionsAdapter(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getKeys(boolean deep) {
|
||||||
|
return config.getKeys(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getValues(boolean deep) {
|
||||||
|
return config.getValues(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(String path) {
|
||||||
|
return config.contains(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSet(String path) {
|
||||||
|
return config.isSet(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentPath() {
|
||||||
|
return config.getCurrentPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return config.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Configuration getRoot() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigurationSection getParent() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addDefault(String path, Object value) {
|
||||||
|
config.addDefault(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigurationSection getDefaultSection() {
|
||||||
|
return config.getDefaultSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(String path, Object value) {
|
||||||
|
config.set(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(String path) {
|
||||||
|
return config.get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(String path, Object def) {
|
||||||
|
return config.get(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigurationSection createSection(String path) {
|
||||||
|
return config.createSection(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigurationSection createSection(String path, Map<?, ?> map) {
|
||||||
|
return config.createSection(path, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other non-FileConfiguration methods
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createDefaultSection(@NotNull String path) {
|
||||||
|
return config.createDefaultSection(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createDefaultSection(@NotNull String path, String... comment) {
|
||||||
|
return config.createDefaultSection(path, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createDefaultSection(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||||
|
return config.createDefaultSection(path, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setComment(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, String... lines) {
|
||||||
|
return config.setComment(path, commentStyle, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setComment(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> lines) {
|
||||||
|
return config.setComment(path, commentStyle, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefaultComment(@NotNull String path, String... lines) {
|
||||||
|
return config.setDefaultComment(path, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefaultComment(@NotNull String path, @Nullable List<String> lines) {
|
||||||
|
return config.setDefaultComment(path, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefaultComment(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, String... lines) {
|
||||||
|
return config.setDefaultComment(path, commentStyle, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefaultComment(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> lines) {
|
||||||
|
return config.setDefaultComment(path, commentStyle, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Comment getComment(@NotNull String path) {
|
||||||
|
return config.getComment(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getCommentString(@NotNull String path) {
|
||||||
|
return config.getCommentString(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public List<ConfigSection> getSections(String path) {
|
||||||
|
return config.getSections(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection set(@NotNull String path, @Nullable Object value, String... comment) {
|
||||||
|
return config.set(path, value, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection set(@NotNull String path, @Nullable Object value, List<String> comment) {
|
||||||
|
return config.set(path, value, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection set(@NotNull String path, @Nullable Object value, @Nullable ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||||
|
return config.set(path, value, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection set(@NotNull String path, @Nullable Object value, @Nullable ConfigFormattingRules.CommentStyle commentStyle, List<String> comment) {
|
||||||
|
return config.set(path, value, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value) {
|
||||||
|
return config.setDefault(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, String... comment) {
|
||||||
|
return config.setDefault(path, value, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, List<String> comment) {
|
||||||
|
return config.setDefault(path, value, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||||
|
return config.setDefault(path, value, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, ConfigFormattingRules.CommentStyle commentStyle, List<String> comment) {
|
||||||
|
return config.setDefault(path, value, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createSection(@NotNull String path, String... comment) {
|
||||||
|
return config.createSection(path, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createSection(@NotNull String path, @Nullable List<String> comment) {
|
||||||
|
return config.createSection(path, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createSection(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||||
|
return config.createSection(path, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createSection(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> comment) {
|
||||||
|
return config.createSection(path, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getChar(@NotNull String path) {
|
||||||
|
return config.getChar(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getChar(@NotNull String path, char def) {
|
||||||
|
return config.getChar(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public CompatibleMaterial getMaterial(@NotNull String path) {
|
||||||
|
return config.getMaterial(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public CompatibleMaterial getMaterial(@NotNull String path, @Nullable CompatibleMaterial def) {
|
||||||
|
return config.getMaterial(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection getOrCreateConfigurationSection(@NotNull String path) {
|
||||||
|
return config.getOrCreateConfigurationSection(path);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.songoda.core.configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ConfigFormattingRules {
|
||||||
|
int spacesBetweenMainCategories;
|
||||||
|
int spacesBetweenValues;
|
||||||
|
CommentStyle rootCommentStyle = CommentStyle.BLOCKSPACED;
|
||||||
|
CommentStyle mainCategoryCommentStyle = CommentStyle.SPACED;
|
||||||
|
|
||||||
|
public enum CommentStyle {
|
||||||
|
/**
|
||||||
|
* # Comment
|
||||||
|
*/
|
||||||
|
SIMPLE(false, false, " ", ""),
|
||||||
|
/**
|
||||||
|
* # <br />
|
||||||
|
* # Comment <br />
|
||||||
|
* # <br />
|
||||||
|
*/
|
||||||
|
SPACED(false, true, " ", ""),
|
||||||
|
/**
|
||||||
|
* ########### <br />
|
||||||
|
* # Comment # <br />
|
||||||
|
* ########### <br />
|
||||||
|
*/
|
||||||
|
BLOCKED(true, false, " ", " "),
|
||||||
|
/**
|
||||||
|
* ############# <br />
|
||||||
|
* #|¯¯¯¯¯¯¯¯¯|# <br />
|
||||||
|
* #| Comment |# <br />
|
||||||
|
* #|_________|# <br />
|
||||||
|
* ############# <br />
|
||||||
|
*/
|
||||||
|
BLOCKSPACED(true, true, "|\u00AF", '\u00AF', "\u00AF|", "| ", " |", "|_", '_', "_|");
|
||||||
|
|
||||||
|
final boolean drawBorder, drawSpace;
|
||||||
|
final String commentPrefix, spacePrefixTop, spacePrefixBottom;
|
||||||
|
final String commentSuffix, spaceSuffixTop, spaceSuffixBottom;
|
||||||
|
final char spaceCharTop, spaceCharBottom;
|
||||||
|
|
||||||
|
CommentStyle(boolean drawBorder, boolean drawSpace,
|
||||||
|
String spacePrefixTop, char spaceCharTop, String spaceSuffixTop,
|
||||||
|
String commentPrefix, String commentSuffix,
|
||||||
|
String spacePrefixBottom, char spaceCharBottom, String spaceSuffixBottom) {
|
||||||
|
this.drawBorder = drawBorder;
|
||||||
|
this.drawSpace = drawSpace;
|
||||||
|
this.commentPrefix = commentPrefix;
|
||||||
|
this.spacePrefixTop = spacePrefixTop;
|
||||||
|
this.spacePrefixBottom = spacePrefixBottom;
|
||||||
|
this.commentSuffix = commentSuffix;
|
||||||
|
this.spaceSuffixTop = spaceSuffixTop;
|
||||||
|
this.spaceSuffixBottom = spaceSuffixBottom;
|
||||||
|
this.spaceCharTop = spaceCharTop;
|
||||||
|
this.spaceCharBottom = spaceCharBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommentStyle(boolean drawBorder, boolean drawSpace, String commentPrefix, String commentSuffix) {
|
||||||
|
this.drawBorder = drawBorder;
|
||||||
|
this.drawSpace = drawSpace;
|
||||||
|
this.commentPrefix = commentPrefix;
|
||||||
|
this.commentSuffix = commentSuffix;
|
||||||
|
this.spacePrefixTop = this.spacePrefixBottom = "";
|
||||||
|
this.spaceCharTop = this.spaceCharBottom = ' ';
|
||||||
|
this.spaceSuffixTop = this.spaceSuffixBottom = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CommentStyle parseStyle(List<String> lines) {
|
||||||
|
if (lines == null || lines.size() <= 2) {
|
||||||
|
return CommentStyle.SIMPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lines.get(0).trim().equals("#") && lines.get(lines.size() - 1).trim().equals("#")) {
|
||||||
|
return CommentStyle.SPACED;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasBorders = lines.get(0).trim().matches("^##+$") && lines.get(lines.size() - 1).trim().matches("^##+$");
|
||||||
|
if (!hasBorders) {
|
||||||
|
// default return
|
||||||
|
return CommentStyle.SIMPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now need to figure out if this is blocked or not
|
||||||
|
if (lines.size() > 4 && lines.get(1).trim().matches(("^#"
|
||||||
|
+ CommentStyle.BLOCKSPACED.spacePrefixTop + CommentStyle.BLOCKSPACED.spaceCharTop + "+"
|
||||||
|
+ CommentStyle.BLOCKSPACED.spaceSuffixTop + "#$").replace("|", "\\|"))
|
||||||
|
&& lines.get(1).trim().matches(("^#"
|
||||||
|
+ CommentStyle.BLOCKSPACED.spacePrefixTop + CommentStyle.BLOCKSPACED.spaceCharTop + "+"
|
||||||
|
+ CommentStyle.BLOCKSPACED.spaceSuffixTop + "#$").replace("|", "\\|"))) {
|
||||||
|
return CommentStyle.BLOCKSPACED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommentStyle.BLOCKED;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.songoda.core.configuration;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.file.FileConfigurationOptions;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ConfigOptionsAdapter extends FileConfigurationOptions {
|
||||||
|
final ConfigSection config;
|
||||||
|
|
||||||
|
public ConfigOptionsAdapter(ConfigSection config) {
|
||||||
|
super(config);
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config getConfig() {
|
||||||
|
return (Config) config.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ConfigFileConfigurationAdapter configuration() {
|
||||||
|
return new ConfigFileConfigurationAdapter((Config) config.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ConfigOptionsAdapter copyDefaults(boolean value) {
|
||||||
|
// we always copy new values
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ConfigOptionsAdapter pathSeparator(char value) {
|
||||||
|
(config.root).setPathSeparator(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ConfigOptionsAdapter header(@Nullable String value) {
|
||||||
|
if (value == null) {
|
||||||
|
((Config) config.root).setHeader((List) null);
|
||||||
|
} else {
|
||||||
|
((Config) config.root).setHeader(value.split("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ConfigOptionsAdapter copyHeader(boolean value) {
|
||||||
|
if (!value) {
|
||||||
|
((Config) config.root).setHeader((List) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indent() {
|
||||||
|
return config.root.getIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigOptionsAdapter indent(int value) {
|
||||||
|
config.root.setIndent(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,761 @@
|
||||||
|
package com.songoda.core.configuration;
|
||||||
|
|
||||||
|
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||||
|
import org.bukkit.configuration.Configuration;
|
||||||
|
import org.bukkit.configuration.MemoryConfiguration;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for a specific node
|
||||||
|
*/
|
||||||
|
public class ConfigSection extends MemoryConfiguration {
|
||||||
|
final String fullPath, nodeKey;
|
||||||
|
final ConfigSection root;
|
||||||
|
final ConfigSection parent;
|
||||||
|
protected int indentation = 2; // between 2 and 9 (inclusive)
|
||||||
|
protected char pathChar = '.';
|
||||||
|
final HashMap<String, Comment> configComments;
|
||||||
|
final HashMap<String, Comment> defaultComments;
|
||||||
|
final LinkedHashMap<String, Object> defaults;
|
||||||
|
final LinkedHashMap<String, Object> values;
|
||||||
|
/**
|
||||||
|
* Internal root state: if any configuration value has changed from file state
|
||||||
|
*/
|
||||||
|
boolean changed = false;
|
||||||
|
final boolean isDefault;
|
||||||
|
final Object lock = new Object();
|
||||||
|
|
||||||
|
ConfigSection() {
|
||||||
|
this.root = this;
|
||||||
|
this.parent = null;
|
||||||
|
isDefault = false;
|
||||||
|
nodeKey = fullPath = "";
|
||||||
|
|
||||||
|
configComments = new HashMap<>();
|
||||||
|
defaultComments = new HashMap<>();
|
||||||
|
defaults = new LinkedHashMap<>();
|
||||||
|
values = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSection(ConfigSection root, ConfigSection parent, String nodeKey, boolean isDefault) {
|
||||||
|
this.root = root;
|
||||||
|
this.parent = parent;
|
||||||
|
this.nodeKey = nodeKey;
|
||||||
|
this.fullPath = nodeKey != null ? parent.fullPath + nodeKey + root.pathChar : parent.fullPath;
|
||||||
|
this.isDefault = isDefault;
|
||||||
|
configComments = defaultComments = null;
|
||||||
|
defaults = null;
|
||||||
|
values = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndent() {
|
||||||
|
return root.indentation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndent(int indentation) {
|
||||||
|
root.indentation = indentation;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onChange() {
|
||||||
|
if (parent != null) {
|
||||||
|
root.onChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the character used to separate configuration nodes. <br>
|
||||||
|
* IMPORTANT: Do not change this after loading or adding ConfigurationSections!
|
||||||
|
*
|
||||||
|
* @param pathChar character to use
|
||||||
|
*/
|
||||||
|
public void setPathSeparator(char pathChar) {
|
||||||
|
if (!root.values.isEmpty() || !root.defaults.isEmpty()) {
|
||||||
|
throw new RuntimeException("Path change after config initialization");
|
||||||
|
}
|
||||||
|
|
||||||
|
root.pathChar = pathChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getPathSeparator() {
|
||||||
|
return root.pathChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The full key for this section node
|
||||||
|
*/
|
||||||
|
public String getKey() {
|
||||||
|
return !fullPath.endsWith(String.valueOf(root.pathChar)) ? fullPath : fullPath.substring(0, fullPath.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The specific key that was used from the last node to get to this node
|
||||||
|
*/
|
||||||
|
public String getNodeKey() {
|
||||||
|
return nodeKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the path required for this node to exist. <br />
|
||||||
|
* <b>DO NOT USE THIS IN A SYNCHRONIZED LOCK</b>
|
||||||
|
*
|
||||||
|
* @param path full path of the node required. Eg, for foo.bar.node, this will create sections for foo and foo.bar
|
||||||
|
* @param useDefault set to true if this is a default value
|
||||||
|
*/
|
||||||
|
protected void createNodePath(@NotNull String path, boolean useDefault) {
|
||||||
|
if (path.indexOf(root.pathChar) != -1) {
|
||||||
|
// if any intermediate nodes don't exist, create them
|
||||||
|
String[] pathParts = path.split(Pattern.quote(String.valueOf(root.pathChar)));
|
||||||
|
StringBuilder nodePath = new StringBuilder(fullPath);
|
||||||
|
LinkedHashMap<String, Object> writeTo = useDefault ? root.defaults : root.values;
|
||||||
|
ConfigSection travelNode = this;
|
||||||
|
|
||||||
|
synchronized (root.lock) {
|
||||||
|
for (int i = 0; i < pathParts.length - 1; ++i) {
|
||||||
|
final String node = (i != 0 ? nodePath.append(root.pathChar) : nodePath).append(pathParts[i]).toString();
|
||||||
|
|
||||||
|
if (!(writeTo.get(node) instanceof ConfigSection)) {
|
||||||
|
writeTo.put(node, travelNode = new ConfigSection(root, travelNode, pathParts[i], useDefault));
|
||||||
|
} else {
|
||||||
|
travelNode = (ConfigSection) writeTo.get(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createDefaultSection(@NotNull String path) {
|
||||||
|
createNodePath(path, true);
|
||||||
|
ConfigSection section = new ConfigSection(root, this, path, true);
|
||||||
|
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.defaults.put(fullPath + path, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createDefaultSection(@NotNull String path, String... comment) {
|
||||||
|
createNodePath(path, true);
|
||||||
|
ConfigSection section = new ConfigSection(root, this, path, true);
|
||||||
|
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.defaults.put(fullPath + path, section);
|
||||||
|
root.defaultComments.put(fullPath + path, new Comment(comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createDefaultSection(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||||
|
createNodePath(path, true);
|
||||||
|
ConfigSection section = new ConfigSection(root, this, path, true);
|
||||||
|
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.defaults.put(fullPath + path, section);
|
||||||
|
root.defaultComments.put(fullPath + path, new Comment(commentStyle, comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setComment(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, String... lines) {
|
||||||
|
return setComment(path, lines != null ? new Comment(commentStyle, lines) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setComment(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> lines) {
|
||||||
|
return setComment(path, lines != null ? new Comment(commentStyle, lines) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setComment(@NotNull String path, @Nullable Comment comment) {
|
||||||
|
synchronized (root.lock) {
|
||||||
|
if (isDefault) {
|
||||||
|
root.defaultComments.put(fullPath + path, comment);
|
||||||
|
} else {
|
||||||
|
root.configComments.put(fullPath + path, comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefaultComment(@NotNull String path, String... lines) {
|
||||||
|
return setDefaultComment(path, lines.length == 0 ? null : Arrays.asList(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefaultComment(@NotNull String path, @Nullable List<String> lines) {
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.defaultComments.put(fullPath + path, new Comment(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefaultComment(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, String... lines) {
|
||||||
|
return setDefaultComment(path, commentStyle, lines.length == 0 ? null : Arrays.asList(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefaultComment(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> lines) {
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.defaultComments.put(fullPath + path, new Comment(commentStyle, lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefaultComment(@NotNull String path, @Nullable Comment comment) {
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.defaultComments.put(fullPath + path, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Comment getComment(@NotNull String path) {
|
||||||
|
Comment result = root.configComments.get(fullPath + path);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
result = root.defaultComments.get(fullPath + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getCommentString(@NotNull String path) {
|
||||||
|
Comment result = root.configComments.get(fullPath + path);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
result = root.defaultComments.get(fullPath + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result != null ? result.toString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addDefault(@NotNull String path, @Nullable Object value) {
|
||||||
|
createNodePath(path, true);
|
||||||
|
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.defaults.put(fullPath + path, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addDefaults(@NotNull Map<String, Object> defaults) {
|
||||||
|
//defaults.entrySet().stream().forEach(m -> root.defaults.put(fullPath + m.getKey(), m.getValue()));
|
||||||
|
defaults.entrySet().forEach(m -> addDefault(m.getKey(), m.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDefaults(Configuration c) {
|
||||||
|
if (fullPath.isEmpty()) {
|
||||||
|
root.defaults.clear();
|
||||||
|
} else {
|
||||||
|
root.defaults.keySet().stream()
|
||||||
|
.filter(k -> k.startsWith(fullPath))
|
||||||
|
.forEach(root.defaults::remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
addDefaults(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigSection getDefaults() {
|
||||||
|
return new ConfigSection(root, this, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigSection getDefaultSection() {
|
||||||
|
return new ConfigSection(root, this, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigOptionsAdapter options() {
|
||||||
|
return new ConfigOptionsAdapter(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<String> getKeys(boolean deep) {
|
||||||
|
LinkedHashSet<String> result = new LinkedHashSet<>();
|
||||||
|
int pathIndex = fullPath.lastIndexOf(root.pathChar);
|
||||||
|
|
||||||
|
if (deep) {
|
||||||
|
result.addAll(root.defaults.keySet().stream()
|
||||||
|
.filter(k -> k.startsWith(fullPath))
|
||||||
|
.map(k -> !k.endsWith(String.valueOf(root.pathChar)) ? k.substring(pathIndex + 1) : k.substring(pathIndex + 1, k.length() - 1))
|
||||||
|
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||||
|
result.addAll(root.values.keySet().stream()
|
||||||
|
.filter(k -> k.startsWith(fullPath))
|
||||||
|
.map(k -> !k.endsWith(String.valueOf(root.pathChar)) ? k.substring(pathIndex + 1) : k.substring(pathIndex + 1, k.length() - 1))
|
||||||
|
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||||
|
} else {
|
||||||
|
result.addAll(root.defaults.keySet().stream()
|
||||||
|
.filter(k -> k.startsWith(fullPath) && k.lastIndexOf(root.pathChar) == pathIndex)
|
||||||
|
.map(k -> !k.endsWith(String.valueOf(root.pathChar)) ? k.substring(pathIndex + 1) : k.substring(pathIndex + 1, k.length() - 1))
|
||||||
|
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||||
|
result.addAll(root.values.keySet().stream()
|
||||||
|
.filter(k -> k.startsWith(fullPath) && k.lastIndexOf(root.pathChar) == pathIndex)
|
||||||
|
.map(k -> !k.endsWith(String.valueOf(root.pathChar)) ? k.substring(pathIndex + 1) : k.substring(pathIndex + 1, k.length() - 1))
|
||||||
|
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getValues(boolean deep) {
|
||||||
|
LinkedHashMap<String, Object> result = new LinkedHashMap<>();
|
||||||
|
int pathIndex = fullPath.lastIndexOf(root.pathChar);
|
||||||
|
|
||||||
|
if (deep) {
|
||||||
|
result.putAll((Map<String, Object>) root.defaults.entrySet().stream()
|
||||||
|
.filter(k -> k.getKey().startsWith(fullPath))
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
e -> !e.getKey().endsWith(String.valueOf(root.pathChar)) ? e.getKey().substring(pathIndex + 1) : e.getKey().substring(pathIndex + 1, e.getKey().length() - 1),
|
||||||
|
Map.Entry::getValue,
|
||||||
|
(v1, v2) -> {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}, // never going to be merging keys
|
||||||
|
LinkedHashMap::new)));
|
||||||
|
|
||||||
|
result.putAll((Map<String, Object>) root.values.entrySet().stream()
|
||||||
|
.filter(k -> k.getKey().startsWith(fullPath))
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
e -> !e.getKey().endsWith(String.valueOf(root.pathChar)) ? e.getKey().substring(pathIndex + 1) : e.getKey().substring(pathIndex + 1, e.getKey().length() - 1),
|
||||||
|
Map.Entry::getValue,
|
||||||
|
(v1, v2) -> {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}, // never going to be merging keys
|
||||||
|
LinkedHashMap::new)));
|
||||||
|
} else {
|
||||||
|
result.putAll((Map<String, Object>) root.defaults.entrySet().stream()
|
||||||
|
.filter(k -> k.getKey().startsWith(fullPath) && k.getKey().lastIndexOf(root.pathChar) == pathIndex)
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
e -> !e.getKey().endsWith(String.valueOf(root.pathChar)) ? e.getKey().substring(pathIndex + 1) : e.getKey().substring(pathIndex + 1, e.getKey().length() - 1),
|
||||||
|
Map.Entry::getValue,
|
||||||
|
(v1, v2) -> {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}, // never going to be merging keys
|
||||||
|
LinkedHashMap::new)));
|
||||||
|
|
||||||
|
result.putAll((Map<String, Object>) root.values.entrySet().stream()
|
||||||
|
.filter(k -> k.getKey().startsWith(fullPath) && k.getKey().lastIndexOf(root.pathChar) == pathIndex)
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
e -> !e.getKey().endsWith(String.valueOf(root.pathChar)) ? e.getKey().substring(pathIndex + 1) : e.getKey().substring(pathIndex + 1, e.getKey().length() - 1),
|
||||||
|
Map.Entry::getValue,
|
||||||
|
(v1, v2) -> {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}, // never going to be merging keys
|
||||||
|
LinkedHashMap::new)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public List<ConfigSection> getSections(String path) {
|
||||||
|
ConfigSection rootSection = getConfigurationSection(path);
|
||||||
|
|
||||||
|
if (rootSection == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<ConfigSection> result = new ArrayList<>();
|
||||||
|
rootSection.getKeys(false).stream()
|
||||||
|
.map(rootSection::get)
|
||||||
|
.filter(ConfigSection.class::isInstance)
|
||||||
|
.forEachOrdered(object -> result.add((ConfigSection) object));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@NotNull String path) {
|
||||||
|
return root.defaults.containsKey(fullPath + path) || root.values.containsKey(fullPath + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@NotNull String path, boolean ignoreDefault) {
|
||||||
|
return (!ignoreDefault && root.defaults.containsKey(fullPath + path)) || root.values.containsKey(fullPath + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSet(@NotNull String path) {
|
||||||
|
return root.defaults.get(fullPath + path) != null || root.values.get(fullPath + path) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentPath() {
|
||||||
|
return fullPath.isEmpty() ? "" : fullPath.substring(0, fullPath.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
if (fullPath.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = fullPath.split(Pattern.quote(String.valueOf(root.pathChar)));
|
||||||
|
return parts[parts.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigSection getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigSection getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object get(@NotNull String path) {
|
||||||
|
Object result = root.values.get(fullPath + path);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
result = root.defaults.get(fullPath + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object get(@NotNull String path, @Nullable Object def) {
|
||||||
|
Object result = root.values.get(fullPath + path);
|
||||||
|
|
||||||
|
return result != null ? result : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@NotNull String path, @Nullable Object value) {
|
||||||
|
if (isDefault) {
|
||||||
|
addDefault(path, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
createNodePath(path, false);
|
||||||
|
Object last;
|
||||||
|
synchronized (root.lock) {
|
||||||
|
if (value != null) {
|
||||||
|
root.changed |= (last = root.values.put(fullPath + path, value)) != value;
|
||||||
|
} else {
|
||||||
|
root.changed |= (last = root.values.remove(fullPath + path)) != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last != value && last instanceof ConfigSection) {
|
||||||
|
// clean up orphaned nodes
|
||||||
|
final String trim = fullPath + path + root.pathChar;
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.values.keySet().stream()
|
||||||
|
.filter(k -> k.startsWith(trim))
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
.forEach(root.values::remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection set(@NotNull String path, @Nullable Object value, String... comment) {
|
||||||
|
set(path, value);
|
||||||
|
return setComment(path, null, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection set(@NotNull String path, @Nullable Object value, List<String> comment) {
|
||||||
|
set(path, value);
|
||||||
|
return setComment(path, null, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection set(@NotNull String path, @Nullable Object value, @Nullable ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||||
|
set(path, value);
|
||||||
|
return setComment(path, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection set(@NotNull String path, @Nullable Object value, @Nullable ConfigFormattingRules.CommentStyle commentStyle, List<String> comment) {
|
||||||
|
set(path, value);
|
||||||
|
return setComment(path, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value) {
|
||||||
|
addDefault(path, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, String... comment) {
|
||||||
|
addDefault(path, value);
|
||||||
|
return setDefaultComment(path, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, List<String> comment) {
|
||||||
|
addDefault(path, value);
|
||||||
|
return setDefaultComment(path, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||||
|
addDefault(path, value);
|
||||||
|
return setDefaultComment(path, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, ConfigFormattingRules.CommentStyle commentStyle, List<String> comment) {
|
||||||
|
addDefault(path, value);
|
||||||
|
return setDefaultComment(path, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ConfigSection createSection(@NotNull String path) {
|
||||||
|
createNodePath(path, false);
|
||||||
|
ConfigSection section = new ConfigSection(root, this, path, false);
|
||||||
|
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.values.put(fullPath + path, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
root.changed = true;
|
||||||
|
onChange();
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createSection(@NotNull String path, String... comment) {
|
||||||
|
return createSection(path, null, comment.length == 0 ? null : Arrays.asList(comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createSection(@NotNull String path, @Nullable List<String> comment) {
|
||||||
|
return createSection(path, null, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createSection(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||||
|
return createSection(path, commentStyle, comment.length == 0 ? null : Arrays.asList(comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection createSection(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> comment) {
|
||||||
|
createNodePath(path, false);
|
||||||
|
ConfigSection section = new ConfigSection(root, this, path, false);
|
||||||
|
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.values.put(fullPath + path, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
setComment(path, commentStyle, comment);
|
||||||
|
root.changed = true;
|
||||||
|
onChange();
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ConfigSection createSection(@NotNull String path, Map<?, ?> map) {
|
||||||
|
createNodePath(path, false);
|
||||||
|
ConfigSection section = new ConfigSection(root, this, path, false);
|
||||||
|
|
||||||
|
synchronized (root.lock) {
|
||||||
|
root.values.put(fullPath + path, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||||
|
if (entry.getValue() instanceof Map) {
|
||||||
|
section.createSection(entry.getKey().toString(), (Map<?, ?>) entry.getValue());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.set(entry.getKey().toString(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
root.changed = true;
|
||||||
|
onChange();
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getString(@NotNull String path) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result != null ? result.toString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getString(@NotNull String path, @Nullable String def) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result != null ? result.toString() : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getChar(@NotNull String path) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result != null && !result.toString().isEmpty() ? result.toString().charAt(0) : '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getChar(@NotNull String path, char def) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result != null && !result.toString().isEmpty() ? result.toString().charAt(0) : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getInt(@NotNull String path) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof Number ? ((Number) result).intValue() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getInt(@NotNull String path, int def) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof Number ? ((Number) result).intValue() : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBoolean(@NotNull String path) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof Boolean ? (Boolean) result : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBoolean(@NotNull String path, boolean def) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof Boolean ? (Boolean) result : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getDouble(@NotNull String path) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof Number ? ((Number) result).doubleValue() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getDouble(@NotNull String path, double def) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof Number ? ((Number) result).doubleValue() : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLong(@NotNull String path) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof Number ? ((Number) result).longValue() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLong(@NotNull String path, long def) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof Number ? ((Number) result).longValue() : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<?> getList(@NotNull String path) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof List ? (List<?>) result : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<?> getList(@NotNull String path, @Nullable List<?> def) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof List ? (List<?>) result : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public CompatibleMaterial getMaterial(@NotNull String path) {
|
||||||
|
String val = getString(path);
|
||||||
|
|
||||||
|
return val != null ? CompatibleMaterial.getMaterial(val) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public CompatibleMaterial getMaterial(@NotNull String path, @Nullable CompatibleMaterial def) {
|
||||||
|
String val = getString(path);
|
||||||
|
|
||||||
|
CompatibleMaterial mat = val != null ? CompatibleMaterial.getMaterial(val) : null;
|
||||||
|
|
||||||
|
return mat != null ? mat : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public <T> T getObject(@NotNull String path, @NotNull Class<T> clazz) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return clazz.isInstance(result) ? clazz.cast(result) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public <T> T getObject(@NotNull String path, @NotNull Class<T> clazz, @Nullable T def) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return clazz.isInstance(result) ? clazz.cast(result) : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigSection getConfigurationSection(@NotNull String path) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof ConfigSection ? (ConfigSection) result : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ConfigSection getOrCreateConfigurationSection(@NotNull String path) {
|
||||||
|
Object result = get(path);
|
||||||
|
|
||||||
|
return result instanceof ConfigSection ? (ConfigSection) result : createSection(path);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package com.songoda.core.configuration;
|
||||||
|
|
||||||
|
import com.songoda.core.SongodaCore;
|
||||||
|
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class ConfigSetting {
|
||||||
|
final Config config;
|
||||||
|
final String key;
|
||||||
|
|
||||||
|
public ConfigSetting(@NotNull Config config, @NotNull String key) {
|
||||||
|
this.config = config;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigSetting(@NotNull Config config, @NotNull String key, @NotNull Object defaultValue, String... comment) {
|
||||||
|
this.config = config;
|
||||||
|
this.key = key;
|
||||||
|
|
||||||
|
config.setDefault(key, defaultValue, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigSetting(@NotNull Config config, @NotNull String key, @NotNull Object defaultValue, ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||||
|
this.config = config;
|
||||||
|
this.key = key;
|
||||||
|
|
||||||
|
config.setDefault(key, defaultValue, commentStyle, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getIntegerList() {
|
||||||
|
return config.getIntegerList(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getStringList() {
|
||||||
|
return config.getStringList(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getBoolean() {
|
||||||
|
return config.getBoolean(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getBoolean(boolean def) {
|
||||||
|
return config.getBoolean(key, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInt() {
|
||||||
|
return config.getInt(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInt(int def) {
|
||||||
|
return config.getInt(key, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLong() {
|
||||||
|
return config.getLong(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLong(long def) {
|
||||||
|
return config.getLong(key, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDouble() {
|
||||||
|
return config.getDouble(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDouble(double def) {
|
||||||
|
return config.getDouble(key, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString() {
|
||||||
|
return config.getString(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String def) {
|
||||||
|
return config.getString(key, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getObject() {
|
||||||
|
return config.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getObject(Object def) {
|
||||||
|
return config.get(key, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getObject(@NotNull Class<T> clazz) {
|
||||||
|
return config.getObject(key, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getObject(@NotNull Class<T> clazz, @Nullable T def) {
|
||||||
|
return config.getObject(key, clazz, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getChar() {
|
||||||
|
return config.getChar(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public char getChar(char def) {
|
||||||
|
return config.getChar(key, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public CompatibleMaterial getMaterial() {
|
||||||
|
String val = config.getString(key);
|
||||||
|
CompatibleMaterial mat = CompatibleMaterial.getMaterial(config.getString(key));
|
||||||
|
|
||||||
|
if (mat == null) {
|
||||||
|
SongodaCore.getLogger().log(Level.WARNING, String.format("Config value \"%s\" has an invalid material name: \"%s\"", key, val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return mat != null ? mat : CompatibleMaterial.STONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public CompatibleMaterial getMaterial(@NotNull CompatibleMaterial def) {
|
||||||
|
//return config.getMaterial(key, def);
|
||||||
|
String val = config.getString(key);
|
||||||
|
CompatibleMaterial mat = val != null ? CompatibleMaterial.getMaterial(val) : null;
|
||||||
|
|
||||||
|
if (mat == null) {
|
||||||
|
SongodaCore.getLogger().log(Level.WARNING, String.format("Config value \"%s\" has an invalid material name: \"%s\"", key, val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return mat != null ? mat : def;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.songoda.core.configuration;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
|
||||||
|
public interface DataStoreObject<T> {
|
||||||
|
/**
|
||||||
|
* @return a unique hashable instance of T to store this value under
|
||||||
|
*/
|
||||||
|
T getKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a unique identifier for saving this value with
|
||||||
|
*/
|
||||||
|
String getConfigKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save this data to a ConfigurationSection
|
||||||
|
*/
|
||||||
|
void saveToSection(ConfigurationSection sec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this data has changed from the state saved to file
|
||||||
|
*/
|
||||||
|
boolean hasChanged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark this data as needing a save or not
|
||||||
|
*/
|
||||||
|
void setChanged(boolean isChanged);
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
package com.songoda.core.configuration;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public interface HeaderCommentable {
|
|
||||||
void setHeaderComment(@Nullable Supplier<String> comment);
|
|
||||||
|
|
||||||
default void setHeaderComment(@Nullable String comment) {
|
|
||||||
setHeaderComment(() -> comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable Supplier<String> getHeaderComment();
|
|
||||||
|
|
||||||
@NotNull String generateHeaderCommentLines();
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
package com.songoda.core.configuration;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.Writer;
|
|
||||||
|
|
||||||
public interface IConfiguration {
|
|
||||||
/**
|
|
||||||
* This method returns whether a given key is set memory, ignoring its possibly null value.
|
|
||||||
*
|
|
||||||
* {@link #set(String, Object)}
|
|
||||||
* {@link #unset(String)}
|
|
||||||
*/
|
|
||||||
boolean has(String key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns the value for a given key.
|
|
||||||
* A value of null can mean that the key does not exist or that the value is null.
|
|
||||||
*
|
|
||||||
* @see #has(String)
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
Object get(String key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is mostly identical to {@link #get(String)}
|
|
||||||
* but returns the given default value if the key doesn't exist or the value is null.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
Object getOr(String key, @Nullable Object defaultValue);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method sets a given key to a given value in memory.
|
|
||||||
*
|
|
||||||
* @return The previous value associated with key, or null if there was no mapping for key
|
|
||||||
*
|
|
||||||
* @see #save(Writer)
|
|
||||||
*/
|
|
||||||
Object set(@NotNull String key, @Nullable Object value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method removes the given key from memory together with its value.
|
|
||||||
*
|
|
||||||
* @return The previous value associated with key, or null if there was no mapping for key
|
|
||||||
*/
|
|
||||||
Object unset(String key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method clears all the configuration values from memory that have been loaded or set.
|
|
||||||
*
|
|
||||||
* @see #load(Reader)
|
|
||||||
*/
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method parses and loads the configuration and stores them as key-value pairs in memory.
|
|
||||||
* Keys that are not loaded with this call but still exist in memory, are removed.
|
|
||||||
* Additional data may be read depending on the implementation (e.g. comments).
|
|
||||||
*
|
|
||||||
* @see #reset()
|
|
||||||
*/
|
|
||||||
void load(Reader reader) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method serializes the key-value pairs in memory and writes them to the given writer.
|
|
||||||
* Additional data may be written depending on the implementation (e.g. comments).
|
|
||||||
*/
|
|
||||||
void save(Writer writer) throws IOException;
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package com.songoda.core.configuration;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public interface NodeCommentable {
|
|
||||||
void setNodeComment(@NotNull String key, @Nullable Supplier<String> comment);
|
|
||||||
|
|
||||||
default void setNodeComment(@NotNull String key, @Nullable String comment) {
|
|
||||||
setNodeComment(key, () -> comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable Supplier<String> getNodeComment(@Nullable String key);
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
package com.songoda.core.configuration;
|
|
||||||
|
|
||||||
import com.songoda.core.utils.Pair;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ReadOnlyConfigEntry implements ConfigEntry {
|
|
||||||
protected final @NotNull IConfiguration config;
|
|
||||||
protected final @NotNull String key;
|
|
||||||
|
|
||||||
public ReadOnlyConfigEntry(@NotNull IConfiguration config, @NotNull String key) {
|
|
||||||
this.config = config;
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull String getKey() {
|
|
||||||
return this.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull IConfiguration getConfig() {
|
|
||||||
return this.config;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract(" -> null")
|
|
||||||
public @Nullable Object getDefaultValue() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract("_ -> fail")
|
|
||||||
public void setDefaultValue(@Nullable Object defaultValue) {
|
|
||||||
throw new UnsupportedOperationException("Cannot set defaultValue on a read-only config entry");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract("_ -> fail")
|
|
||||||
public ConfigEntry withDefaultValue(@Nullable Object defaultValue) {
|
|
||||||
throw new UnsupportedOperationException("Cannot set defaultValue on a read-only config entry");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract("_ -> fail")
|
|
||||||
public ConfigEntry withComment(Supplier<String> comment) {
|
|
||||||
throw new UnsupportedOperationException("Cannot set comment on a read-only config entry");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract(" -> null")
|
|
||||||
public Map<Integer, Pair<String, Function<Object, Object>>> getUpgradeSteps() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract("_, _, _ -> fail")
|
|
||||||
public ConfigEntry withUpgradeStep(int version, @Nullable String keyInGivenVersion, @Nullable Function<Object, Object> valueConverter) {
|
|
||||||
throw new UnsupportedOperationException("Cannot set upgrade step on a read-only config entry");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract("_ -> fail")
|
|
||||||
public void set(@Nullable Object value) {
|
|
||||||
throw new UnsupportedOperationException("Cannot set value on a read-only config entry");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,284 @@
|
||||||
|
package com.songoda.core.configuration;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to easily store a set of one data value
|
||||||
|
*
|
||||||
|
* @param <T> DataObject class that is used to store the data
|
||||||
|
*/
|
||||||
|
public class SimpleDataStore<T extends DataStoreObject> {
|
||||||
|
protected final Plugin plugin;
|
||||||
|
protected final String filename, dirName;
|
||||||
|
private final Function<ConfigurationSection, T> getFromSection;
|
||||||
|
protected final HashMap<Object, T> data = new HashMap<>();
|
||||||
|
private File file;
|
||||||
|
private final Object lock = new Object();
|
||||||
|
SaveTask saveTask;
|
||||||
|
Timer autosaveTimer;
|
||||||
|
/**
|
||||||
|
* time in seconds to start a save after a change is made
|
||||||
|
*/
|
||||||
|
int autosaveInterval = 60;
|
||||||
|
|
||||||
|
public SimpleDataStore(@NotNull Plugin plugin, @NotNull String filename, @NotNull Function<ConfigurationSection, T> loadFunction) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.filename = filename;
|
||||||
|
dirName = null;
|
||||||
|
this.getFromSection = loadFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleDataStore(@NotNull Plugin plugin, @Nullable String directory, @NotNull String filename, @NotNull Function<ConfigurationSection, T> loadFunction) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.filename = filename;
|
||||||
|
this.dirName = directory;
|
||||||
|
this.getFromSection = loadFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public File getFile() {
|
||||||
|
if (file == null) {
|
||||||
|
if (dirName != null) {
|
||||||
|
this.file = new File(plugin.getDataFolder() + dirName, filename != null ? filename : "data.yml");
|
||||||
|
} else {
|
||||||
|
this.file = new File(plugin.getDataFolder(), filename != null ? filename : "data.yml");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a directly-modifiable instance of the data mapping for this
|
||||||
|
* storage
|
||||||
|
*/
|
||||||
|
public Map<Object, T> getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value to which the specified key is mapped, or {@code null}
|
||||||
|
* if this map contains no mapping for the key.
|
||||||
|
*
|
||||||
|
* @param key key whose mapping is to be retrieved from this storage
|
||||||
|
*
|
||||||
|
* @return the value associated with <tt>key</tt>, or
|
||||||
|
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public T get(Object key) {
|
||||||
|
return data.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the mapping for the specified key from this storage if present.
|
||||||
|
*
|
||||||
|
* @param key key whose mapping is to be removed from this storage
|
||||||
|
*
|
||||||
|
* @return the previous value associated with <tt>key</tt>, or
|
||||||
|
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public T remove(@NotNull Object key) {
|
||||||
|
T temp;
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
temp = data.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
save();
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the mapping for the specified key from this storage if present.
|
||||||
|
*
|
||||||
|
* @param value value whose mapping is to be removed from this storage
|
||||||
|
*
|
||||||
|
* @return the previous value associated with <tt>key</tt>, or
|
||||||
|
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public T remove(@NotNull T value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
T temp;
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
temp = data.remove(value.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
save();
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified value in this storage. If the map previously contained
|
||||||
|
* a mapping for the key, the old value is replaced.
|
||||||
|
*
|
||||||
|
* @param value value to be added
|
||||||
|
*
|
||||||
|
* @return the previous value associated with <tt>value.getKey()</tt>, or
|
||||||
|
* <tt>null</tt> if there was no mapping for <tt>value.getKey()</tt>.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public T add(@NotNull T value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
T temp;
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
temp = data.put(value.getKey(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
save();
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified value in this storage. If the map previously contained
|
||||||
|
* a mapping for the key, the old value is replaced.
|
||||||
|
*
|
||||||
|
* @param value values to be added
|
||||||
|
*/
|
||||||
|
public void addAll(@NotNull T[] value) {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
for (T t : value) {
|
||||||
|
if (t != null) {
|
||||||
|
data.put(t.getKey(), t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified value in this storage. If the map previously contained
|
||||||
|
* a mapping for the key, the old value is replaced.
|
||||||
|
*
|
||||||
|
* @param value values to be added
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public void addAll(@NotNull Collection<T> value) {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
for (T v : value) {
|
||||||
|
if (v != null) {
|
||||||
|
data.put(v.getKey(), v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load data from the associated file
|
||||||
|
*/
|
||||||
|
public void load() {
|
||||||
|
if (!getFile().exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
YamlConfiguration f = new YamlConfiguration();
|
||||||
|
f.options().pathSeparator('\0');
|
||||||
|
f.load(file);
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
data.clear();
|
||||||
|
|
||||||
|
f.getValues(false).values().stream()
|
||||||
|
.filter(ConfigurationSection.class::isInstance)
|
||||||
|
.map(v -> getFromSection.apply((ConfigurationSection) v))
|
||||||
|
.forEach(v -> data.put(v.getKey(), v));
|
||||||
|
}
|
||||||
|
} catch (IOException | InvalidConfigurationException ex) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Failed to load data from " + file.getName(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally save this storage's data to file if there have been changes
|
||||||
|
* made
|
||||||
|
*/
|
||||||
|
public void saveChanges() {
|
||||||
|
if (saveTask != null || data.values().stream().anyMatch(DataStoreObject::hasChanged)) {
|
||||||
|
flushSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save this file data. This saves later asynchronously.
|
||||||
|
*/
|
||||||
|
public void save() {
|
||||||
|
// save async even if no plugin or if plugin disabled
|
||||||
|
if (saveTask == null) {
|
||||||
|
autosaveTimer = new Timer((plugin != null ? plugin.getName() + "-DataStoreSave-" : "DataStoreSave-") + getFile().getName());
|
||||||
|
autosaveTimer.schedule(saveTask = new SaveTask(), autosaveInterval * 1000L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a new save of this storage's data
|
||||||
|
*/
|
||||||
|
public void flushSave() {
|
||||||
|
if (saveTask != null) {
|
||||||
|
//Close Threads
|
||||||
|
saveTask.cancel();
|
||||||
|
autosaveTimer.cancel();
|
||||||
|
saveTask = null;
|
||||||
|
autosaveTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
YamlConfiguration f = new YamlConfiguration();
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
data.values().forEach(e -> e.saveToSection(f.createSection(e.getConfigKey())));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
f.save(getFile());
|
||||||
|
data.values().forEach(e -> e.setChanged(false));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Failed to save data to " + file.getName(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SaveTask extends TimerTask {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
flushSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +0,0 @@
|
||||||
package com.songoda.core.configuration;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public interface WriteableConfigEntry extends ConfigEntry {
|
|
||||||
@Override
|
|
||||||
default void set(@Nullable Object value) {
|
|
||||||
getConfig().set(getKey(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default ConfigEntry withComment(Supplier<String> comment) {
|
|
||||||
((NodeCommentable) getConfig()).setNodeComment(getKey(), comment);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.songoda.core.configuration.editor;
|
package com.songoda.core.configuration.editor;
|
||||||
|
|
||||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||||
|
import com.songoda.core.configuration.Config;
|
||||||
import com.songoda.core.gui.Gui;
|
import com.songoda.core.gui.Gui;
|
||||||
import com.songoda.core.gui.GuiUtils;
|
import com.songoda.core.gui.GuiUtils;
|
||||||
import com.songoda.core.gui.SimplePagedGui;
|
import com.songoda.core.gui.SimplePagedGui;
|
||||||
|
@ -26,10 +27,7 @@ import java.util.logging.Level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit a configuration file for a specific plugin
|
* Edit a configuration file for a specific plugin
|
||||||
*
|
|
||||||
* @deprecated Needs a recode in another package
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public class ConfigEditorGui extends SimplePagedGui {
|
public class ConfigEditorGui extends SimplePagedGui {
|
||||||
final JavaPlugin plugin;
|
final JavaPlugin plugin;
|
||||||
final String file;
|
final String file;
|
||||||
|
@ -275,9 +273,9 @@ public class ConfigEditorGui extends SimplePagedGui {
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to save config changes to " + file, ex);
|
plugin.getLogger().log(Level.SEVERE, "Failed to save config changes to " + file, ex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}/* else if (config instanceof Config) {
|
} else if (config instanceof Config) {
|
||||||
((Config) config).save();
|
((Config) config).save();
|
||||||
}*/ else {
|
} else {
|
||||||
player.sendMessage(ChatColor.RED + "Unknown configuration type '" + config.getClass().getName() + "' - Please report this error!");
|
player.sendMessage(ChatColor.RED + "Unknown configuration type '" + config.getClass().getName() + "' - Please report this error!");
|
||||||
plugin.getLogger().log(Level.WARNING, "Unknown configuration type '" + config.getClass().getName() + "' - Please report this error!");
|
plugin.getLogger().log(Level.WARNING, "Unknown configuration type '" + config.getClass().getName() + "' - Please report this error!");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -12,10 +12,7 @@ import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit a string list
|
* Edit a string list
|
||||||
*
|
|
||||||
* @deprecated Needs a recode in another package
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public class ConfigEditorListEditorGui extends SimplePagedGui {
|
public class ConfigEditorListEditorGui extends SimplePagedGui {
|
||||||
final ConfigEditorGui current;
|
final ConfigEditorGui current;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.songoda.core.configuration.editor;
|
||||||
|
|
||||||
import com.songoda.core.SongodaPlugin;
|
import com.songoda.core.SongodaPlugin;
|
||||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||||
|
import com.songoda.core.configuration.Config;
|
||||||
import com.songoda.core.gui.Gui;
|
import com.songoda.core.gui.Gui;
|
||||||
import com.songoda.core.gui.GuiUtils;
|
import com.songoda.core.gui.GuiUtils;
|
||||||
import com.songoda.core.gui.SimplePagedGui;
|
import com.songoda.core.gui.SimplePagedGui;
|
||||||
|
@ -18,10 +19,7 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit all configuration files for a specific plugin
|
* Edit all configuration files for a specific plugin
|
||||||
*
|
|
||||||
* @deprecated Needs a recode in another package
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public class PluginConfigGui extends SimplePagedGui {
|
public class PluginConfigGui extends SimplePagedGui {
|
||||||
final JavaPlugin plugin;
|
final JavaPlugin plugin;
|
||||||
LinkedHashMap<String, MemoryConfiguration> configs = new LinkedHashMap<>();
|
LinkedHashMap<String, MemoryConfiguration> configs = new LinkedHashMap<>();
|
||||||
|
@ -35,18 +33,14 @@ public class PluginConfigGui extends SimplePagedGui {
|
||||||
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
||||||
// FIXME: Add SongodaCore config
|
|
||||||
// FIXME: Add plugin configs
|
|
||||||
plugin.getLogger().warning("Loading configs for " + plugin.getName() + " is not supported at the moment.");
|
|
||||||
|
|
||||||
// collect list of plugins
|
// collect list of plugins
|
||||||
// configs.put(plugin.getCoreConfig().getFile().getName(), plugin.getCoreConfig());
|
configs.put(plugin.getCoreConfig().getFile().getName(), plugin.getCoreConfig());
|
||||||
// List<Config> more = plugin.getConfigs();
|
List<Config> more = plugin.getExtraConfig();
|
||||||
// if (more != null && !more.isEmpty()) {
|
if (more != null && !more.isEmpty()) {
|
||||||
// for (Config cfg : more) {
|
for (Config cfg : more) {
|
||||||
// configs.put(cfg.getFile().getName(), cfg);
|
configs.put(cfg.getFile().getName(), cfg);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,245 +0,0 @@
|
||||||
package com.songoda.core.configuration.songoda;
|
|
||||||
|
|
||||||
import com.songoda.core.configuration.ConfigEntry;
|
|
||||||
import com.songoda.core.configuration.ReadOnlyConfigEntry;
|
|
||||||
import com.songoda.core.configuration.yaml.YamlConfigEntry;
|
|
||||||
import com.songoda.core.configuration.yaml.YamlConfiguration;
|
|
||||||
import com.songoda.core.utils.Pair;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
// TODO: replace all config related exceptions with custom exceptions
|
|
||||||
// TODO: Allow registering load-Listeners
|
|
||||||
// TODO: Provide method to only save if changed
|
|
||||||
public class SongodaYamlConfig extends YamlConfiguration {
|
|
||||||
protected static final String CANNOT_CREATE_BACKUP_COPY_EXCEPTION_PREFIX = "Unable to create backup copy of config file: ";
|
|
||||||
|
|
||||||
public final @NotNull File file;
|
|
||||||
protected final @NotNull Logger logger;
|
|
||||||
|
|
||||||
private int targetVersion;
|
|
||||||
private ConfigEntry versionEntry;
|
|
||||||
|
|
||||||
protected final Map<String, ConfigEntry> configEntries = new LinkedHashMap<>(0);
|
|
||||||
|
|
||||||
public SongodaYamlConfig(@NotNull JavaPlugin plugin, @NotNull File file) {
|
|
||||||
this(file, plugin.getLogger());
|
|
||||||
}
|
|
||||||
|
|
||||||
public SongodaYamlConfig(@NotNull JavaPlugin plugin, @NotNull String fileName) {
|
|
||||||
this(new File(plugin.getDataFolder(), fileName), plugin.getLogger());
|
|
||||||
}
|
|
||||||
|
|
||||||
public SongodaYamlConfig(@NotNull File file) {
|
|
||||||
this(file, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SongodaYamlConfig(@NotNull File file, @Nullable Logger logger) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.file = Objects.requireNonNull(file);
|
|
||||||
this.logger = logger != null ? logger : Logger.getLogger(getClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls {@link #load()} and then {@link #save()}.<br>
|
|
||||||
* <br>
|
|
||||||
* As this is intended to keep the {@link org.bukkit.plugin.java.JavaPlugin#onEnable()} method clean,
|
|
||||||
* it catches all exceptions and logs them instead.<br>
|
|
||||||
* <br>
|
|
||||||
* If this method returns false, the plugins should be disabled.
|
|
||||||
*
|
|
||||||
* @return true if the load and save were successful, false if an exception was thrown.
|
|
||||||
*
|
|
||||||
* @see #save()
|
|
||||||
* @see #load()
|
|
||||||
*/
|
|
||||||
public boolean init() {
|
|
||||||
try {
|
|
||||||
this.load();
|
|
||||||
this.save();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
this.logger.log(Level.SEVERE, "Failed to load config file: " + this.file.getPath(), ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyConfigEntry getReadEntry(@NotNull String key) {
|
|
||||||
return new ReadOnlyConfigEntry(this, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigEntry createEntry(@NotNull String key) {
|
|
||||||
return createEntry(key, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigEntry createEntry(@NotNull String key, @Nullable Object defaultValue) {
|
|
||||||
ConfigEntry entry = new YamlConfigEntry(this, key, defaultValue);
|
|
||||||
|
|
||||||
if (this.configEntries.putIfAbsent(key, entry) != null) {
|
|
||||||
throw new IllegalArgumentException("Entry already exists for key: " + key);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.get() == null) {
|
|
||||||
entry.set(defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SongodaYamlConfig withVersion(int version) {
|
|
||||||
return withVersion("version", version, () -> "Don't touch this – it's used to track the version of the config.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public SongodaYamlConfig withVersion(@NotNull String key, int version, @Nullable Supplier<String> comment) {
|
|
||||||
if (version < 0) {
|
|
||||||
throw new IllegalArgumentException("Version must be positive");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.versionEntry != null) {
|
|
||||||
this.versionEntry.set(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.targetVersion = version;
|
|
||||||
|
|
||||||
this.versionEntry = new YamlConfigEntry(this, key, 0);
|
|
||||||
this.versionEntry.withComment(comment);
|
|
||||||
this.versionEntry.set(this.targetVersion);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load() throws IOException {
|
|
||||||
try (Reader reader = Files.newBufferedReader(this.file.toPath(), StandardCharsets.UTF_8)) {
|
|
||||||
load(reader);
|
|
||||||
} catch (FileNotFoundException ignore) {
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new IOException("Unable to load '" + this.file.getPath() + "'", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() throws IOException {
|
|
||||||
Files.createDirectories(this.file.toPath().getParent());
|
|
||||||
|
|
||||||
|
|
||||||
try (Writer writer = Files.newBufferedWriter(this.file.toPath(), StandardCharsets.UTF_8)) {
|
|
||||||
super.save(writer);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new IOException("Unable to save '" + this.file.getPath() + "'", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void load(Reader reader) throws IOException {
|
|
||||||
super.load(reader);
|
|
||||||
|
|
||||||
upgradeOldConfigVersion();
|
|
||||||
|
|
||||||
for (ConfigEntry entry : this.configEntries.values()) {
|
|
||||||
if (entry.get() == null && entry.getDefaultValue() != null) {
|
|
||||||
entry.set(entry.getDefaultValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return false, if no config version is set or no upgrade is needed
|
|
||||||
*/
|
|
||||||
protected boolean upgradeOldConfigVersion() throws IOException {
|
|
||||||
if (this.versionEntry == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.versionEntry.getInt() > this.targetVersion) {
|
|
||||||
throw new IllegalStateException("Cannot upgrade a config version that is higher than the target version");
|
|
||||||
}
|
|
||||||
if (this.versionEntry.getInt() == this.targetVersion) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
createBackupCopyFile();
|
|
||||||
|
|
||||||
while (this.versionEntry.getInt() < this.targetVersion) {
|
|
||||||
upgradeOldConfigVersionByOne();
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanValuesMap(this.values);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void upgradeOldConfigVersionByOne() {
|
|
||||||
int currentVersion = this.versionEntry.getInt();
|
|
||||||
int targetVersion = currentVersion + 1;
|
|
||||||
|
|
||||||
if (targetVersion > this.targetVersion) {
|
|
||||||
throw new IllegalStateException("Cannot upgrade a config version that is higher than the target version");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ConfigEntry entry : this.configEntries.values()) {
|
|
||||||
if (entry.getUpgradeSteps() == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Pair<@Nullable String, @Nullable Function<Object, Object>> upgradeStep = entry.getUpgradeSteps().get(currentVersion);
|
|
||||||
if (upgradeStep == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String oldEntryKey = upgradeStep.getFirst();
|
|
||||||
if (oldEntryKey == null) {
|
|
||||||
oldEntryKey = entry.getKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
Object newValue = get(oldEntryKey);
|
|
||||||
if (upgradeStep.getSecond() != null) {
|
|
||||||
newValue = upgradeStep.getSecond().apply(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
set(oldEntryKey, null);
|
|
||||||
entry.set(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.versionEntry.set(targetVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void createBackupCopyFile() throws IOException {
|
|
||||||
if (!this.file.exists()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Path targetPath = this.file.toPath().resolveSibling(this.file.getPath() + ".backup-" + System.currentTimeMillis());
|
|
||||||
|
|
||||||
Files.copy(
|
|
||||||
this.file.toPath(),
|
|
||||||
targetPath,
|
|
||||||
StandardCopyOption.REPLACE_EXISTING
|
|
||||||
);
|
|
||||||
|
|
||||||
this.logger.info("Created backup copy of config file '" + this.file.getPath() + "' to '" + targetPath + "'");
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new IOException(CANNOT_CREATE_BACKUP_COPY_EXCEPTION_PREFIX + this.file.getPath(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
package com.songoda.core.configuration.yaml;
|
|
||||||
|
|
||||||
import org.yaml.snakeyaml.DumperOptions;
|
|
||||||
import org.yaml.snakeyaml.comments.CommentLine;
|
|
||||||
import org.yaml.snakeyaml.comments.CommentType;
|
|
||||||
import org.yaml.snakeyaml.events.CommentEvent;
|
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
|
||||||
import org.yaml.snakeyaml.nodes.Node;
|
|
||||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
|
||||||
import org.yaml.snakeyaml.nodes.ScalarNode;
|
|
||||||
import org.yaml.snakeyaml.representer.Representer;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class YamlCommentRepresenter extends Representer {
|
|
||||||
private final Map<String, Supplier<String>> nodeComments;
|
|
||||||
|
|
||||||
public YamlCommentRepresenter(DumperOptions dumperOptions, Map<String, Supplier<String>> nodeComments) {
|
|
||||||
super(dumperOptions);
|
|
||||||
this.nodeComments = nodeComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Node represent(Object data) {
|
|
||||||
Node rootNode = super.represent(data);
|
|
||||||
|
|
||||||
if (!(rootNode instanceof MappingNode)) {
|
|
||||||
return rootNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (NodeTuple nodeTuple : ((MappingNode) rootNode).getValue()) {
|
|
||||||
if (!(nodeTuple.getKeyNode() instanceof ScalarNode)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
applyComment((ScalarNode) nodeTuple.getKeyNode(), ((ScalarNode) nodeTuple.getKeyNode()).getValue());
|
|
||||||
|
|
||||||
if (nodeTuple.getValueNode() instanceof MappingNode) {
|
|
||||||
String key = ((ScalarNode) nodeTuple.getKeyNode()).getValue();
|
|
||||||
|
|
||||||
resolveSubNodes(((MappingNode) nodeTuple.getValueNode()), key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rootNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void resolveSubNodes(MappingNode mappingNode, String key) {
|
|
||||||
for (NodeTuple nodeTuple : mappingNode.getValue()) {
|
|
||||||
if (!(nodeTuple.getKeyNode() instanceof ScalarNode)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String newKey = key + "." + ((ScalarNode) nodeTuple.getKeyNode()).getValue();
|
|
||||||
|
|
||||||
applyComment((ScalarNode) nodeTuple.getKeyNode(), newKey);
|
|
||||||
|
|
||||||
if (nodeTuple.getValueNode() instanceof MappingNode) {
|
|
||||||
resolveSubNodes(((MappingNode) nodeTuple.getValueNode()), newKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void applyComment(ScalarNode scalarNode, String key) {
|
|
||||||
Supplier<String> innerValue = this.nodeComments.get(key);
|
|
||||||
|
|
||||||
if (innerValue != null) {
|
|
||||||
scalarNode.setBlockComments(Collections.singletonList(new CommentLine(new CommentEvent(CommentType.BLOCK, " " + innerValue.get(), null, null))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
package com.songoda.core.configuration.yaml;
|
|
||||||
|
|
||||||
import com.songoda.core.configuration.ConfigEntry;
|
|
||||||
import com.songoda.core.configuration.IConfiguration;
|
|
||||||
import com.songoda.core.configuration.WriteableConfigEntry;
|
|
||||||
import com.songoda.core.utils.Pair;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class YamlConfigEntry implements WriteableConfigEntry {
|
|
||||||
protected final @NotNull YamlConfiguration config;
|
|
||||||
protected final @NotNull String key;
|
|
||||||
protected @Nullable Object defaultValue;
|
|
||||||
|
|
||||||
protected @Nullable Map<Integer, Pair<@Nullable String, @Nullable Function<@Nullable Object, @Nullable Object>>> upgradeSteps;
|
|
||||||
|
|
||||||
public YamlConfigEntry(@NotNull YamlConfiguration config, @NotNull String key, @Nullable Object defaultValue) {
|
|
||||||
this.config = config;
|
|
||||||
this.key = key;
|
|
||||||
this.defaultValue = defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull String getKey() {
|
|
||||||
return this.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull IConfiguration getConfig() {
|
|
||||||
return this.config;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Object getDefaultValue() {
|
|
||||||
return this.defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Map<Integer, Pair<@Nullable String, @Nullable Function<@Nullable Object, @Nullable Object>>> getUpgradeSteps() {
|
|
||||||
return this.upgradeSteps;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDefaultValue(@Nullable Object defaultValue) {
|
|
||||||
this.defaultValue = defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConfigEntry withDefaultValue(@Nullable Object defaultValue) {
|
|
||||||
this.setDefaultValue(defaultValue);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConfigEntry withUpgradeStep(int version, @Nullable String keyInGivenVersion, @Nullable Function<@Nullable Object, @Nullable Object> valueConverter) {
|
|
||||||
if (keyInGivenVersion == null && valueConverter == null) {
|
|
||||||
throw new IllegalArgumentException("You must provide either a key or a value converter");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.upgradeSteps == null) {
|
|
||||||
this.upgradeSteps = new HashMap<>(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.upgradeSteps.put(version, new Pair<>(keyInGivenVersion, valueConverter));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
|
|
||||||
YamlConfigEntry that = (YamlConfigEntry) o;
|
|
||||||
return this.config.equals(that.config) &&
|
|
||||||
this.key.equals(that.key) &&
|
|
||||||
Objects.equals(this.defaultValue, that.defaultValue) &&
|
|
||||||
Objects.equals(this.upgradeSteps, that.upgradeSteps);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(this.config, this.key, this.defaultValue, this.upgradeSteps);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,371 +0,0 @@
|
||||||
package com.songoda.core.configuration.yaml;
|
|
||||||
|
|
||||||
import com.songoda.core.configuration.HeaderCommentable;
|
|
||||||
import com.songoda.core.configuration.IConfiguration;
|
|
||||||
import com.songoda.core.configuration.NodeCommentable;
|
|
||||||
import org.apache.commons.lang.ArrayUtils;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.yaml.snakeyaml.DumperOptions;
|
|
||||||
import org.yaml.snakeyaml.LoaderOptions;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
import org.yaml.snakeyaml.constructor.Constructor;
|
|
||||||
import org.yaml.snakeyaml.representer.Representer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
// TODO: Allow registering own custom value converter (e.g. Bukkit-Location to Map and back)
|
|
||||||
// + move the huge block from #set into such a converter and register it by default
|
|
||||||
public class YamlConfiguration implements IConfiguration, HeaderCommentable, NodeCommentable {
|
|
||||||
protected final @NotNull Yaml yaml;
|
|
||||||
protected final @NotNull DumperOptions yamlDumperOptions;
|
|
||||||
protected final @NotNull YamlCommentRepresenter yamlCommentRepresenter;
|
|
||||||
|
|
||||||
protected final @NotNull Map<String, Object> values;
|
|
||||||
protected final @NotNull Map<String, Supplier<String>> nodeComments;
|
|
||||||
protected @Nullable Supplier<String> headerComment;
|
|
||||||
|
|
||||||
public YamlConfiguration() {
|
|
||||||
this(new LinkedHashMap<>(), new LinkedHashMap<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected YamlConfiguration(@NotNull Map<String, Object> values, @NotNull Map<String, Supplier<String>> nodeComments) {
|
|
||||||
this.values = Objects.requireNonNull(values);
|
|
||||||
this.nodeComments = Objects.requireNonNull(nodeComments);
|
|
||||||
|
|
||||||
this.yamlDumperOptions = createDefaultYamlDumperOptions();
|
|
||||||
this.yamlCommentRepresenter = new YamlCommentRepresenter(this.yamlDumperOptions, this.nodeComments);
|
|
||||||
this.yaml = createDefaultYaml(this.yamlDumperOptions, this.yamlCommentRepresenter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract(pure = true, value = "null -> false")
|
|
||||||
public boolean has(String key) {
|
|
||||||
if (key == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] fullKeyPath = key.split("\\.");
|
|
||||||
|
|
||||||
Map<String, ?> innerMap = getInnerMap(this.values, Arrays.copyOf(fullKeyPath, fullKeyPath.length - 1), false);
|
|
||||||
|
|
||||||
if (innerMap != null) {
|
|
||||||
return innerMap.containsKey(fullKeyPath[fullKeyPath.length - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract(pure = true, value = "null -> null")
|
|
||||||
public @Nullable Object get(String key) {
|
|
||||||
if (key == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return getInnerValueForKey(this.values, key);
|
|
||||||
} catch (IllegalArgumentException ignore) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Contract(pure = true, value = "null,_ -> param2")
|
|
||||||
public @Nullable Object getOr(String key, @Nullable Object fallbackValue) {
|
|
||||||
Object value = get(key);
|
|
||||||
|
|
||||||
return value == null ? fallbackValue : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull Set<String> getKeys(String key) {
|
|
||||||
if (key == null) {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.equals("")) {
|
|
||||||
return Collections.unmodifiableSet(this.values.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, ?> innerMap = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
innerMap = getInnerMap(this.values, key.split("\\."), false);
|
|
||||||
} catch (IllegalArgumentException ignore) {
|
|
||||||
}
|
|
||||||
|
|
||||||
if (innerMap != null) {
|
|
||||||
return Collections.unmodifiableSet(innerMap.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.emptySet();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object set(@NotNull String key, @Nullable Object value) {
|
|
||||||
if (value != null) {
|
|
||||||
if (value instanceof Float) {
|
|
||||||
value = ((Float) value).doubleValue();
|
|
||||||
} else if (value instanceof Character) {
|
|
||||||
value = ((Character) value).toString();
|
|
||||||
} else if (value.getClass().isEnum()) {
|
|
||||||
value = ((Enum<?>) value).name();
|
|
||||||
} else if (value.getClass().isArray()) {
|
|
||||||
if (value instanceof int[]) {
|
|
||||||
value = Arrays.asList(ArrayUtils.toObject((int[]) value));
|
|
||||||
} else if (value instanceof long[]) {
|
|
||||||
value = Arrays.asList(ArrayUtils.toObject((long[]) value));
|
|
||||||
} else if (value instanceof short[]) {
|
|
||||||
List<Integer> newValue = new ArrayList<>(((short[]) value).length);
|
|
||||||
for (Short s : (short[]) value) {
|
|
||||||
newValue.add(s.intValue());
|
|
||||||
}
|
|
||||||
value = newValue;
|
|
||||||
} else if (value instanceof byte[]) {
|
|
||||||
List<Integer> newValue = new ArrayList<>(((byte[]) value).length);
|
|
||||||
for (Byte b : (byte[]) value) {
|
|
||||||
newValue.add(b.intValue());
|
|
||||||
}
|
|
||||||
value = newValue;
|
|
||||||
} else if (value instanceof double[]) {
|
|
||||||
value = Arrays.asList(ArrayUtils.toObject((double[]) value));
|
|
||||||
} else if (value instanceof float[]) {
|
|
||||||
List<Double> newValue = new ArrayList<>(((float[]) value).length);
|
|
||||||
for (float f : (float[]) value) {
|
|
||||||
newValue.add(new Float(f).doubleValue());
|
|
||||||
}
|
|
||||||
value = newValue;
|
|
||||||
} else if (value instanceof boolean[]) {
|
|
||||||
value = Arrays.asList(ArrayUtils.toObject((boolean[]) value));
|
|
||||||
} else if (value instanceof char[]) {
|
|
||||||
List<String> newValue = new ArrayList<>(((char[]) value).length);
|
|
||||||
for (char c : (char[]) value) {
|
|
||||||
newValue.add(String.valueOf(c));
|
|
||||||
}
|
|
||||||
value = newValue;
|
|
||||||
} else {
|
|
||||||
value = Arrays.asList((Object[]) value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return setInnerValueForKey(this.values, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object unset(String key) {
|
|
||||||
String[] fullKeyPath = key.split("\\.");
|
|
||||||
|
|
||||||
Map<String, ?> innerMap = getInnerMap(this.values, Arrays.copyOf(fullKeyPath, fullKeyPath.length - 1), false);
|
|
||||||
|
|
||||||
if (innerMap != null) {
|
|
||||||
return innerMap.remove(fullKeyPath[fullKeyPath.length - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
this.values.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void load(Reader reader) throws IOException {
|
|
||||||
Object yamlData = this.yaml.load(reader);
|
|
||||||
if (yamlData == null) {
|
|
||||||
yamlData = Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(yamlData instanceof Map)) {
|
|
||||||
throw new IllegalStateException("The YAML file does not have the expected tree structure: " + yamlData.getClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (this.values) {
|
|
||||||
this.values.clear();
|
|
||||||
|
|
||||||
for (Map.Entry<?, ?> yamlEntry : ((Map<?, ?>) yamlData).entrySet()) {
|
|
||||||
this.values.put(yamlEntry.getKey().toString(), yamlEntry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save(Writer writer) throws IOException {
|
|
||||||
String headerCommentLines = generateHeaderCommentLines();
|
|
||||||
writer.write(headerCommentLines);
|
|
||||||
|
|
||||||
cleanValuesMap(this.values);
|
|
||||||
|
|
||||||
if (this.values.size() > 0) {
|
|
||||||
if (headerCommentLines.length() > 0) {
|
|
||||||
writer.write(this.yamlDumperOptions.getLineBreak().getString());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.yaml.dump(this.values, writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHeaderComment(@Nullable Supplier<String> comment) {
|
|
||||||
this.headerComment = comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Supplier<String> getHeaderComment() {
|
|
||||||
return this.headerComment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull String generateHeaderCommentLines() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
String headerCommentString = this.headerComment == null ? null : this.headerComment.get();
|
|
||||||
if (headerCommentString != null) {
|
|
||||||
for (String commentLine : headerCommentString.split("\r?\n")) {
|
|
||||||
sb.append("# ")
|
|
||||||
.append(commentLine)
|
|
||||||
.append(this.yamlDumperOptions.getLineBreak().getString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setNodeComment(@NotNull String key, @Nullable Supplier<String> comment) {
|
|
||||||
this.nodeComments.put(key, comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Supplier<String> getNodeComment(@Nullable String key) {
|
|
||||||
return this.nodeComments.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toYamlString() throws IOException {
|
|
||||||
StringWriter writer = new StringWriter();
|
|
||||||
save(writer);
|
|
||||||
|
|
||||||
return writer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "YamlConfiguration{" +
|
|
||||||
"values=" + this.values +
|
|
||||||
", headerComment=" + this.headerComment +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static DumperOptions createDefaultYamlDumperOptions() {
|
|
||||||
DumperOptions dumperOptions = new DumperOptions();
|
|
||||||
dumperOptions.setProcessComments(true);
|
|
||||||
|
|
||||||
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
|
||||||
dumperOptions.setIndentWithIndicator(true);
|
|
||||||
dumperOptions.setIndicatorIndent(2);
|
|
||||||
|
|
||||||
return dumperOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Yaml createDefaultYaml(DumperOptions dumperOptions, Representer representer) {
|
|
||||||
LoaderOptions yamlOptions = new LoaderOptions();
|
|
||||||
yamlOptions.setAllowDuplicateKeys(false);
|
|
||||||
|
|
||||||
return new Yaml(new Constructor(yamlOptions), representer, dumperOptions, yamlOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Object setInnerValueForKey(@NotNull Map<String, Object> map, @NotNull String key, @Nullable Object value) {
|
|
||||||
String[] fullKeyPath = key.split("\\.");
|
|
||||||
|
|
||||||
Map<String, ?> innerMap = getInnerMap(map, Arrays.copyOf(fullKeyPath, fullKeyPath.length - 1), true);
|
|
||||||
|
|
||||||
return ((Map<String, Object>) innerMap).put(fullKeyPath[fullKeyPath.length - 1], value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Object getInnerValueForKey(@NotNull Map<String, Object> map, @NotNull String key) {
|
|
||||||
String[] fullKeyPath = key.split("\\.");
|
|
||||||
|
|
||||||
Map<String, ?> innerMap = getInnerMap(map, Arrays.copyOf(fullKeyPath, fullKeyPath.length - 1), false);
|
|
||||||
|
|
||||||
if (innerMap != null) {
|
|
||||||
return innerMap.get(fullKeyPath[fullKeyPath.length - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_,_,true -> !null")
|
|
||||||
protected static Map<String, ?> getInnerMap(@NotNull Map<String, ?> map, @NotNull String[] keys, boolean createMissingMaps) {
|
|
||||||
if (keys.length == 0) {
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
int currentKeyIndex = 0;
|
|
||||||
Map<String, ?> currentMap = map;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
Object currentValue = currentMap.get(keys[currentKeyIndex]);
|
|
||||||
|
|
||||||
if (currentValue == null) {
|
|
||||||
if (!createMissingMaps) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentValue = new HashMap<>();
|
|
||||||
((Map<String, Object>) currentMap).put(keys[currentKeyIndex], currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(currentValue instanceof Map)) {
|
|
||||||
if (!createMissingMaps) {
|
|
||||||
throw new IllegalArgumentException("Expected a Map when resolving key '" + String.join(".", keys) + "' at '" + String.join(".", Arrays.copyOf(keys, currentKeyIndex + 1)) + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
currentValue = new HashMap<>();
|
|
||||||
((Map<String, Object>) currentMap).put(keys[currentKeyIndex], currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentKeyIndex == keys.length - 1) {
|
|
||||||
return (Map<String, ?>) currentValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentMap = (Map<String, ?>) currentValue;
|
|
||||||
++currentKeyIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This takes a map and removes all keys that have a value of null.<br>
|
|
||||||
* Additionally, if the value is a {@link Map}, it will be recursively cleaned too.<br>
|
|
||||||
* {@link Map}s that are or get empty, will be removed (recursively).<br>
|
|
||||||
*/
|
|
||||||
protected void cleanValuesMap(Map<?, ?> map) {
|
|
||||||
for (Object key : map.keySet().toArray()) {
|
|
||||||
Object value = map.get(key);
|
|
||||||
|
|
||||||
if (value instanceof Map) {
|
|
||||||
cleanValuesMap((Map<?, ?>) value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value == null || (value instanceof Map && ((Map<?, ?>) value).isEmpty())) {
|
|
||||||
map.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +1,64 @@
|
||||||
//package com.songoda.core.core;
|
package com.songoda.core.core;
|
||||||
//
|
|
||||||
//import com.songoda.core.locale.Locale;
|
import com.songoda.core.locale.Locale;
|
||||||
//import org.json.simple.JSONArray;
|
import org.json.simple.JSONArray;
|
||||||
//import org.json.simple.JSONObject;
|
import org.json.simple.JSONObject;
|
||||||
//
|
|
||||||
//import java.io.IOException;
|
import java.io.IOException;
|
||||||
//import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
//import java.net.URL;
|
import java.net.URL;
|
||||||
//import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
//import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
//
|
|
||||||
//public class LocaleModule implements PluginInfoModule {
|
public class LocaleModule implements PluginInfoModule {
|
||||||
// @Override
|
@Override
|
||||||
// public void run(PluginInfo plugin) {
|
public void run(PluginInfo plugin) {
|
||||||
// if (plugin.getJavaPlugin() == null || plugin.getSongodaId() <= 0) {
|
if (plugin.getJavaPlugin() == null || plugin.getSongodaId() <= 0) {
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// try {
|
try {
|
||||||
// JSONObject json = plugin.getJson();
|
JSONObject json = plugin.getJson();
|
||||||
// JSONArray files = (JSONArray) json.get("neededFiles");
|
JSONArray files = (JSONArray) json.get("neededFiles");
|
||||||
//
|
|
||||||
// for (Object o : files) {
|
for (Object o : files) {
|
||||||
// JSONObject file = (JSONObject) o;
|
JSONObject file = (JSONObject) o;
|
||||||
//
|
|
||||||
// if (file.get("type").equals("locale")) {
|
if (file.get("type").equals("locale")) {
|
||||||
// downloadLocale(plugin, (String) file.get("link"), (String) file.get("name"));
|
downloadLocale(plugin, (String) file.get("link"), (String) file.get("name"));
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// } catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
// Logger.getLogger(LocaleModule.class.getName()).log(Level.INFO, "Failed to check for locale files: " + ex.getMessage());
|
Logger.getLogger(LocaleModule.class.getName()).log(Level.INFO, "Failed to check for locale files: " + ex.getMessage());
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// void downloadLocale(PluginInfo plugin, String link, String fileName) throws IOException {
|
void downloadLocale(PluginInfo plugin, String link, String fileName) throws IOException {
|
||||||
// URL url = new URL(link);
|
URL url = new URL(link);
|
||||||
//
|
|
||||||
// HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
||||||
// urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
|
urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
|
||||||
// urlConnection.setRequestProperty("Accept", "*/*");
|
urlConnection.setRequestProperty("Accept", "*/*");
|
||||||
// urlConnection.setInstanceFollowRedirects(true);
|
urlConnection.setInstanceFollowRedirects(true);
|
||||||
// urlConnection.setConnectTimeout(5000);
|
urlConnection.setConnectTimeout(5000);
|
||||||
//
|
|
||||||
// // do we need to follow a redirect?
|
// do we need to follow a redirect?
|
||||||
// int status = urlConnection.getResponseCode();
|
int status = urlConnection.getResponseCode();
|
||||||
// if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER) {
|
if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER) {
|
||||||
// // get redirect url from "location" header field
|
// get redirect url from "location" header field
|
||||||
// String newUrl = urlConnection.getHeaderField("Location");
|
String newUrl = urlConnection.getHeaderField("Location");
|
||||||
// // get the cookie if needed
|
// get the cookie if needed
|
||||||
// String cookies = urlConnection.getHeaderField("Set-Cookie");
|
String cookies = urlConnection.getHeaderField("Set-Cookie");
|
||||||
// // open the new connnection again
|
// open the new connnection again
|
||||||
// urlConnection = (HttpURLConnection) new URL(newUrl).openConnection();
|
urlConnection = (HttpURLConnection) new URL(newUrl).openConnection();
|
||||||
// urlConnection.setRequestProperty("Cookie", cookies);
|
urlConnection.setRequestProperty("Cookie", cookies);
|
||||||
// urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
|
urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
|
||||||
// urlConnection.setRequestProperty("Accept", "*/*");
|
urlConnection.setRequestProperty("Accept", "*/*");
|
||||||
// urlConnection.setConnectTimeout(5000);
|
urlConnection.setConnectTimeout(5000);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// Locale.saveLocale(plugin.getJavaPlugin(), urlConnection.getInputStream(), fileName);
|
Locale.saveLocale(plugin.getJavaPlugin(), urlConnection.getInputStream(), fileName);
|
||||||
//
|
|
||||||
// urlConnection.disconnect();
|
urlConnection.disconnect();
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ package com.songoda.core.gui;
|
||||||
|
|
||||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
import com.songoda.core.compatibility.CompatibleMaterial;
|
||||||
import com.songoda.core.compatibility.ServerVersion;
|
import com.songoda.core.compatibility.ServerVersion;
|
||||||
import com.songoda.core.configuration.ConfigEntry;
|
import com.songoda.core.configuration.Config;
|
||||||
import com.songoda.core.configuration.songoda.SongodaYamlConfig;
|
import com.songoda.core.configuration.ConfigSection;
|
||||||
import com.songoda.core.gui.methods.Clickable;
|
import com.songoda.core.gui.methods.Clickable;
|
||||||
import com.songoda.core.utils.TextUtils;
|
import com.songoda.core.utils.TextUtils;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
@ -16,7 +16,6 @@ import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -46,89 +45,60 @@ public class CustomizableGui extends Gui {
|
||||||
localeFolder.mkdir();
|
localeFolder.mkdir();
|
||||||
}
|
}
|
||||||
|
|
||||||
SongodaYamlConfig config = new SongodaYamlConfig(new File(plugin.getDataFolder(), "gui/" + guiKey + ".yml"));
|
Config config = new Config(plugin, "gui/" + guiKey + ".yml");
|
||||||
try {
|
config.load();
|
||||||
config.load();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// FIXME
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
config.setNodeComment("overrides", "For information on how to apply overrides please visit\n" +
|
config.setNodeComment("overrides", "For information on how to apply overrides please visit\n" +
|
||||||
"https://wiki.craftaro.com/index.php/Gui");
|
"https://wiki.craftaro.com/index.php/Gui");
|
||||||
config.setNodeComment("overrides.example", "This is just an example and does not override to any items in this GUI.");
|
config.setNodeComment("overrides.example", "This is just an example and does not override to any items in this GUI.");
|
||||||
|
|
||||||
config.createEntry("overrides.example.item", CompatibleMaterial.STONE)
|
config.saveChanges();
|
||||||
.withComment("This is the icon material you would like to replace\n" +
|
}
|
||||||
"the current material with.");
|
|
||||||
config.createEntry("overrides.example.position", 5)
|
|
||||||
.withComment("This is the current position of the icon you would like to move.\n" +
|
|
||||||
"The number represents the cell the icon currently resides in.");
|
|
||||||
|
|
||||||
ConfigEntry disabledGuis = config.createEntry("disabled", Arrays.asList("example3", "example4", "example5"))
|
if (!config.isConfigurationSection("disabled")) {
|
||||||
.withComment("All keys on this list will be disabled. You can add any items key here\n" +
|
config.setDefault("disabled", Arrays.asList("example3", "example4", "example5"),
|
||||||
"if you no longer want that item in the GUI.");
|
"All keys on this list will be disabled. You can add any items key here",
|
||||||
|
"if you no longer want that item in the GUI.");
|
||||||
|
|
||||||
try {
|
config.saveChanges();
|
||||||
config.save();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// FIXME
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomContent customContent = loadedGuis.computeIfAbsent(guiKey, g -> new CustomContent(guiKey));
|
CustomContent customContent = loadedGuis.computeIfAbsent(guiKey, g -> new CustomContent(guiKey));
|
||||||
loadedGuis.put(guiKey, customContent);
|
loadedGuis.put(guiKey, customContent);
|
||||||
this.customContent = customContent;
|
this.customContent = customContent;
|
||||||
|
|
||||||
int rows = config.getReadEntry("overrides.__ROWS__").getIntOr(-1);
|
int rows = config.getInt("overrides.__ROWS__", -1);
|
||||||
if (rows != -1) {
|
if (rows != -1) {
|
||||||
customContent.setRows(rows);
|
customContent.setRows(rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String overrideKey : config.getKeys("overrides")) {
|
for (ConfigSection section : config.getSections("overrides")) {
|
||||||
String keyPrefix = "overrides." + overrideKey;
|
if (section.contains("row") ||
|
||||||
|
section.contains("col") ||
|
||||||
ConfigEntry title = config.getReadEntry(keyPrefix + ".title");
|
section.contains("mirrorrow") ||
|
||||||
|
section.contains("mirrorcol")) {
|
||||||
ConfigEntry position = config.getReadEntry(keyPrefix + ".position");
|
if (section.contains("mirrorrow") || section.contains("mirrorcol")) {
|
||||||
|
customContent.addButton(section.getNodeKey(), section.getInt("row", -1),
|
||||||
ConfigEntry row = config.getReadEntry(keyPrefix + ".row");
|
section.getInt("col", -1),
|
||||||
ConfigEntry col = config.getReadEntry(keyPrefix + ".col");
|
section.getBoolean("mirrorrow", false),
|
||||||
|
section.getBoolean("mirrorcol", false),
|
||||||
ConfigEntry mirrorRow = config.getReadEntry(keyPrefix + ".mirrorrow");
|
section.isSet("item") ? CompatibleMaterial.getMaterial(section.getString("item")) : null);
|
||||||
ConfigEntry mirrorCol = config.getReadEntry(keyPrefix + ".mirrorcol");
|
} else {
|
||||||
|
customContent.addButton(section.getNodeKey(), section.getInt("row", -1),
|
||||||
ConfigEntry item = config.getReadEntry(keyPrefix + ".item");
|
section.getInt("col", -1),
|
||||||
ConfigEntry lore = config.getReadEntry(keyPrefix + ".lore");
|
section.getString("title", null),
|
||||||
|
section.isSet("lore") ? section.getStringList("lore") : null,
|
||||||
boolean configHasRowOrColSet = row.has() || col.has();
|
section.isSet("item") ? CompatibleMaterial.getMaterial(section.getString("item")) : null);
|
||||||
boolean configHasMirrorRowOrColSet = mirrorRow.has() || mirrorCol.has();
|
}
|
||||||
|
|
||||||
if (configHasMirrorRowOrColSet) {
|
|
||||||
customContent.addButton(overrideKey,
|
|
||||||
row.getIntOr(-1),
|
|
||||||
col.getIntOr(-1),
|
|
||||||
mirrorRow.getBoolean(),
|
|
||||||
mirrorCol.getBoolean(),
|
|
||||||
item.getMaterial());
|
|
||||||
} else if (configHasRowOrColSet) {
|
|
||||||
customContent.addButton(overrideKey,
|
|
||||||
row.getIntOr(-1),
|
|
||||||
col.getIntOr(-1),
|
|
||||||
title.getString(),
|
|
||||||
lore.getStringList(),
|
|
||||||
item.getMaterial());
|
|
||||||
} else {
|
} else {
|
||||||
customContent.addButton(overrideKey,
|
customContent.addButton(section.getNodeKey(), section.getString("position", "-1"),
|
||||||
position.getStringOr("-1"),
|
section.getString("title", null),
|
||||||
title.getString(),
|
section.isSet("lore") ? section.getStringList("lore") : null,
|
||||||
lore.getStringList(),
|
section.isSet("item") ? CompatibleMaterial.getMaterial(section.getString("item")) : null);
|
||||||
item.getMaterial());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String disabled : disabledGuis.getStringListOr(Collections.emptyList())) {
|
for (String disabled : config.getStringList("disabled")) {
|
||||||
customContent.disableButton(disabled);
|
customContent.disableButton(disabled);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,496 +1,495 @@
|
||||||
//
|
package com.songoda.core.locale;
|
||||||
//package com.songoda.core.locale;
|
|
||||||
//
|
import com.songoda.core.configuration.Config;
|
||||||
//import com.songoda.core.configuration.Config;
|
import com.songoda.core.configuration.ConfigSection;
|
||||||
//import com.songoda.core.configuration.ConfigSection;
|
import com.songoda.core.utils.TextUtils;
|
||||||
//import com.songoda.core.utils.TextUtils;
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
//import org.bukkit.configuration.InvalidConfigurationException;
|
import org.bukkit.plugin.Plugin;
|
||||||
//import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
//import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
//
|
import java.io.BufferedInputStream;
|
||||||
//import java.io.BufferedInputStream;
|
import java.io.BufferedReader;
|
||||||
//import java.io.BufferedReader;
|
import java.io.ByteArrayInputStream;
|
||||||
//import java.io.ByteArrayInputStream;
|
import java.io.File;
|
||||||
//import java.io.File;
|
import java.io.FileInputStream;
|
||||||
//import java.io.FileInputStream;
|
import java.io.FileOutputStream;
|
||||||
//import java.io.FileOutputStream;
|
import java.io.IOException;
|
||||||
//import java.io.IOException;
|
import java.io.InputStream;
|
||||||
//import java.io.InputStream;
|
import java.io.InputStreamReader;
|
||||||
//import java.io.InputStreamReader;
|
import java.io.OutputStream;
|
||||||
//import java.io.OutputStream;
|
import java.nio.charset.Charset;
|
||||||
//import java.nio.charset.Charset;
|
import java.nio.charset.StandardCharsets;
|
||||||
//import java.nio.charset.StandardCharsets;
|
import java.util.ArrayList;
|
||||||
//import java.util.ArrayList;
|
import java.util.HashMap;
|
||||||
//import java.util.HashMap;
|
import java.util.List;
|
||||||
//import java.util.List;
|
import java.util.Map;
|
||||||
//import java.util.Map;
|
import java.util.logging.Level;
|
||||||
//import java.util.logging.Level;
|
import java.util.logging.Logger;
|
||||||
//import java.util.logging.Logger;
|
import java.util.regex.Matcher;
|
||||||
//import java.util.regex.Matcher;
|
import java.util.regex.Pattern;
|
||||||
//import java.util.regex.Pattern;
|
import java.util.stream.Collectors;
|
||||||
//import java.util.stream.Collectors;
|
|
||||||
//
|
/**
|
||||||
///**
|
* Assists in the utilization of localization files.
|
||||||
// * Assists in the utilization of localization files.
|
*/
|
||||||
// */
|
public class Locale {
|
||||||
//public class Locale {
|
private static final Pattern OLD_NODE_PATTERN = Pattern.compile("^([^ ]+)\\s*=\\s*\"?(.*?)\"?$");
|
||||||
// private static final Pattern OLD_NODE_PATTERN = Pattern.compile("^([^ ]+)\\s*=\\s*\"?(.*?)\"?$");
|
private static final String FILE_EXTENSION = ".lang";
|
||||||
// private static final String FILE_EXTENSION = ".lang";
|
|
||||||
//
|
private final Map<String, String> nodes = new HashMap<>();
|
||||||
// private final Map<String, String> nodes = new HashMap<>();
|
private final Plugin plugin;
|
||||||
// private final Plugin plugin;
|
private final File file;
|
||||||
// private final File file;
|
private final String name;
|
||||||
// private final String name;
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Instantiate the Locale class for future use
|
||||||
// * Instantiate the Locale class for future use
|
*
|
||||||
// *
|
* @param plugin Owning Plugin
|
||||||
// * @param plugin Owning Plugin
|
* @param file Location of the locale file
|
||||||
// * @param file Location of the locale file
|
* @param name The locale name for the language
|
||||||
// * @param name The locale name for the language
|
*/
|
||||||
// */
|
public Locale(Plugin plugin, File file, String name) {
|
||||||
// public Locale(Plugin plugin, File file, String name) {
|
this.plugin = plugin;
|
||||||
// this.plugin = plugin;
|
this.file = file;
|
||||||
// this.file = file;
|
this.name = name;
|
||||||
// this.name = name;
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Load a default-included lang file from the plugin's jar file
|
||||||
// * Load a default-included lang file from the plugin's jar file
|
*
|
||||||
// *
|
* @param plugin plugin to load from
|
||||||
// * @param plugin plugin to load from
|
* @param name name of the default locale, eg "en_US"
|
||||||
// * @param name name of the default locale, eg "en_US"
|
*
|
||||||
// *
|
* @return returns the loaded Locale, or null if there was an error
|
||||||
// * @return returns the loaded Locale, or null if there was an error
|
*/
|
||||||
// */
|
public static Locale loadDefaultLocale(JavaPlugin plugin, String name) {
|
||||||
// public static Locale loadDefaultLocale(JavaPlugin plugin, String name) {
|
saveDefaultLocale(plugin, name, name);
|
||||||
// saveDefaultLocale(plugin, name, name);
|
|
||||||
//
|
return loadLocale(plugin, name);
|
||||||
// return loadLocale(plugin, name);
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Load a locale from this plugin's locale directory
|
||||||
// * Load a locale from this plugin's locale directory
|
*
|
||||||
// *
|
* @param plugin plugin to load from
|
||||||
// * @param plugin plugin to load from
|
* @param name name of the locale, eg "en_US"
|
||||||
// * @param name name of the locale, eg "en_US"
|
*
|
||||||
// *
|
* @return returns the loaded Locale, or null if there was an error
|
||||||
// * @return returns the loaded Locale, or null if there was an error
|
*/
|
||||||
// */
|
public static Locale loadLocale(JavaPlugin plugin, String name) {
|
||||||
// public static Locale loadLocale(JavaPlugin plugin, String name) {
|
File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
||||||
// File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
if (!localeFolder.exists()) {
|
||||||
// if (!localeFolder.exists()) {
|
return null;
|
||||||
// return null;
|
}
|
||||||
// }
|
|
||||||
//
|
File localeFile = new File(localeFolder, name + FILE_EXTENSION);
|
||||||
// File localeFile = new File(localeFolder, name + FILE_EXTENSION);
|
if (!localeFolder.exists()) {
|
||||||
// if (!localeFolder.exists()) {
|
return null;
|
||||||
// return null;
|
}
|
||||||
// }
|
|
||||||
//
|
// found the lang file, now load it in!
|
||||||
// // found the lang file, now load it in!
|
Locale l = new Locale(plugin, localeFile, name);
|
||||||
// Locale l = new Locale(plugin, localeFile, name);
|
|
||||||
//
|
if (!l.reloadMessages()) {
|
||||||
// if (!l.reloadMessages()) {
|
return null;
|
||||||
// return null;
|
}
|
||||||
// }
|
|
||||||
//
|
plugin.getLogger().info("Loaded locale \"" + name + "\"");
|
||||||
// plugin.getLogger().info("Loaded locale \"" + name + "\"");
|
|
||||||
//
|
return l;
|
||||||
// return l;
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Load all locales from this plugin's locale directory
|
||||||
// * Load all locales from this plugin's locale directory
|
*
|
||||||
// *
|
* @param plugin plugin to load from
|
||||||
// * @param plugin plugin to load from
|
*
|
||||||
// *
|
* @return returns the loaded Locales
|
||||||
// * @return returns the loaded Locales
|
*/
|
||||||
// */
|
public static List<Locale> loadAllLocales(JavaPlugin plugin) {
|
||||||
// public static List<Locale> loadAllLocales(JavaPlugin plugin) {
|
File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
||||||
// File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
List<Locale> all = new ArrayList<>();
|
||||||
// List<Locale> all = new ArrayList<>();
|
|
||||||
//
|
for (File localeFile : localeFolder.listFiles()) {
|
||||||
// for (File localeFile : localeFolder.listFiles()) {
|
String fileName = localeFile.getName();
|
||||||
// String fileName = localeFile.getName();
|
if (!fileName.endsWith(FILE_EXTENSION)) {
|
||||||
// if (!fileName.endsWith(FILE_EXTENSION)) {
|
continue;
|
||||||
// continue;
|
}
|
||||||
// }
|
|
||||||
//
|
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||||
// fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
if (fileName.split("_").length != 2) {
|
||||||
// if (fileName.split("_").length != 2) {
|
continue;
|
||||||
// continue;
|
}
|
||||||
// }
|
|
||||||
//
|
Locale l = new Locale(plugin, localeFile, fileName);
|
||||||
// Locale l = new Locale(plugin, localeFile, fileName);
|
|
||||||
//
|
if (l.reloadMessages()) {
|
||||||
// if (l.reloadMessages()) {
|
plugin.getLogger().info("Loaded locale \"" + fileName + "\"");
|
||||||
// plugin.getLogger().info("Loaded locale \"" + fileName + "\"");
|
all.add(l);
|
||||||
// all.add(l);
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
return all;
|
||||||
// return all;
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Get a list of all locale files in this plugin's locale directory
|
||||||
// * Get a list of all locale files in this plugin's locale directory
|
*
|
||||||
// *
|
* @param plugin Plugin to check for
|
||||||
// * @param plugin Plugin to check for
|
*/
|
||||||
// */
|
public static List<String> getLocales(Plugin plugin) {
|
||||||
// public static List<String> getLocales(Plugin plugin) {
|
File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
||||||
// File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
List<String> all = new ArrayList<>();
|
||||||
// List<String> all = new ArrayList<>();
|
|
||||||
//
|
for (File localeFile : localeFolder.listFiles()) {
|
||||||
// for (File localeFile : localeFolder.listFiles()) {
|
String fileName = localeFile.getName();
|
||||||
// String fileName = localeFile.getName();
|
if (!fileName.endsWith(FILE_EXTENSION)) {
|
||||||
// if (!fileName.endsWith(FILE_EXTENSION)) {
|
continue;
|
||||||
// continue;
|
}
|
||||||
// }
|
|
||||||
//
|
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||||
// fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
|
||||||
//
|
if (fileName.split("_").length != 2) {
|
||||||
// if (fileName.split("_").length != 2) {
|
continue;
|
||||||
// continue;
|
}
|
||||||
// }
|
|
||||||
//
|
all.add(fileName);
|
||||||
// all.add(fileName);
|
}
|
||||||
// }
|
|
||||||
//
|
return all;
|
||||||
// return all;
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Save a locale file from the Plugin's Resources to the locale folder
|
||||||
// * Save a locale file from the Plugin's Resources to the locale folder
|
*
|
||||||
// *
|
* @param plugin plugin owning the locale file
|
||||||
// * @param plugin plugin owning the locale file
|
* @param locale the specific locale file to save
|
||||||
// * @param locale the specific locale file to save
|
* @param fileName where to save the file
|
||||||
// * @param fileName where to save the file
|
*
|
||||||
// *
|
* @return true if the operation was successful, false otherwise
|
||||||
// * @return true if the operation was successful, false otherwise
|
*/
|
||||||
// */
|
public static boolean saveDefaultLocale(JavaPlugin plugin, String locale, String fileName) {
|
||||||
// public static boolean saveDefaultLocale(JavaPlugin plugin, String locale, String fileName) {
|
return saveLocale(plugin, plugin.getResource(locale + FILE_EXTENSION), fileName, true);
|
||||||
// return saveLocale(plugin, plugin.getResource(locale + FILE_EXTENSION), fileName, true);
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Save a locale file from an InputStream to the locale folder
|
||||||
// * Save a locale file from an InputStream to the locale folder
|
*
|
||||||
// *
|
* @param plugin plugin owning the locale file
|
||||||
// * @param plugin plugin owning the locale file
|
* @param in file to save
|
||||||
// * @param in file to save
|
* @param fileName the name of the file to save
|
||||||
// * @param fileName the name of the file to save
|
*
|
||||||
// *
|
* @return true if the operation was successful, false otherwise
|
||||||
// * @return true if the operation was successful, false otherwise
|
*/
|
||||||
// */
|
public static boolean saveLocale(Plugin plugin, InputStream in, String fileName) {
|
||||||
// public static boolean saveLocale(Plugin plugin, InputStream in, String fileName) {
|
return saveLocale(plugin, in, fileName, false);
|
||||||
// return saveLocale(plugin, in, fileName, false);
|
}
|
||||||
// }
|
|
||||||
//
|
private static boolean saveLocale(Plugin plugin, InputStream in, String fileName, boolean builtin) {
|
||||||
// private static boolean saveLocale(Plugin plugin, InputStream in, String fileName, boolean builtin) {
|
if (in == null) {
|
||||||
// if (in == null) {
|
return false;
|
||||||
// return false;
|
}
|
||||||
// }
|
|
||||||
//
|
File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
||||||
// File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
if (!localeFolder.exists()) localeFolder.mkdirs();
|
||||||
// if (!localeFolder.exists()) localeFolder.mkdirs();
|
|
||||||
//
|
if (!fileName.endsWith(FILE_EXTENSION)) {
|
||||||
// if (!fileName.endsWith(FILE_EXTENSION)) {
|
fileName = fileName + FILE_EXTENSION;
|
||||||
// fileName = fileName + FILE_EXTENSION;
|
}
|
||||||
// }
|
|
||||||
//
|
File destinationFile = new File(localeFolder, fileName);
|
||||||
// File destinationFile = new File(localeFolder, fileName);
|
if (destinationFile.exists()) {
|
||||||
// if (destinationFile.exists()) {
|
return updateFiles(plugin, in, destinationFile, builtin);
|
||||||
// return updateFiles(plugin, in, destinationFile, builtin);
|
}
|
||||||
// }
|
|
||||||
//
|
try (OutputStream outputStream = new FileOutputStream(destinationFile)) {
|
||||||
// try (OutputStream outputStream = new FileOutputStream(destinationFile)) {
|
copy(in, outputStream);
|
||||||
// copy(in, outputStream);
|
|
||||||
//
|
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||||
// fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
|
||||||
//
|
return fileName.split("_").length == 2;
|
||||||
// return fileName.split("_").length == 2;
|
} catch (IOException ignore) {
|
||||||
// } catch (IOException ignore) {
|
}
|
||||||
// }
|
|
||||||
//
|
return false;
|
||||||
// return false;
|
}
|
||||||
// }
|
|
||||||
//
|
// Write new changes to existing files, if any at all
|
||||||
// // Write new changes to existing files, if any at all
|
private static boolean updateFiles(Plugin plugin, InputStream defaultFile, File existingFile, boolean builtin) {
|
||||||
// private static boolean updateFiles(Plugin plugin, InputStream defaultFile, File existingFile, boolean builtin) {
|
try (BufferedInputStream defaultIn = new BufferedInputStream(defaultFile);
|
||||||
// try (BufferedInputStream defaultIn = new BufferedInputStream(defaultFile);
|
BufferedInputStream existingIn = new BufferedInputStream(new FileInputStream(existingFile))) {
|
||||||
// BufferedInputStream existingIn = new BufferedInputStream(new FileInputStream(existingFile))) {
|
|
||||||
//
|
Charset defaultCharset = TextUtils.detectCharset(defaultIn, StandardCharsets.UTF_8);
|
||||||
// Charset defaultCharset = TextUtils.detectCharset(defaultIn, StandardCharsets.UTF_8);
|
Charset existingCharset = TextUtils.detectCharset(existingIn, StandardCharsets.UTF_8);
|
||||||
// Charset existingCharset = TextUtils.detectCharset(existingIn, StandardCharsets.UTF_8);
|
|
||||||
//
|
try (BufferedReader defaultReaderOriginal = new BufferedReader(new InputStreamReader(defaultIn, defaultCharset));
|
||||||
// try (BufferedReader defaultReaderOriginal = new BufferedReader(new InputStreamReader(defaultIn, defaultCharset));
|
BufferedReader existingReaderOriginal = new BufferedReader(new InputStreamReader(existingIn, existingCharset));
|
||||||
// BufferedReader existingReaderOriginal = new BufferedReader(new InputStreamReader(existingIn, existingCharset));
|
BufferedReader defaultReader = translatePropertyToYAML(defaultReaderOriginal, defaultCharset);
|
||||||
// BufferedReader defaultReader = translatePropertyToYAML(defaultReaderOriginal, defaultCharset);
|
BufferedReader existingReader = translatePropertyToYAML(existingReaderOriginal, existingCharset)) {
|
||||||
// BufferedReader existingReader = translatePropertyToYAML(existingReaderOriginal, existingCharset)) {
|
|
||||||
//
|
Config existingLang = new Config(existingFile);
|
||||||
// Config existingLang = new Config(existingFile);
|
existingLang.load(existingReader);
|
||||||
// existingLang.load(existingReader);
|
translateMsgRoot(existingLang, existingFile, existingCharset);
|
||||||
// translateMsgRoot(existingLang, existingFile, existingCharset);
|
|
||||||
//
|
Config defaultLang = new Config();
|
||||||
// Config defaultLang = new Config();
|
String defaultData = defaultReader.lines().map(s -> s.replaceAll("[\uFEFF\uFFFE\u200B]", "")).collect(Collectors.joining("\n"));
|
||||||
// String defaultData = defaultReader.lines().map(s -> s.replaceAll("[\uFEFF\uFFFE\u200B]", "")).collect(Collectors.joining("\n"));
|
defaultLang.loadFromString(defaultData);
|
||||||
// defaultLang.loadFromString(defaultData);
|
translateMsgRoot(defaultLang, defaultData, defaultCharset);
|
||||||
// translateMsgRoot(defaultLang, defaultData, defaultCharset);
|
|
||||||
//
|
List<String> added = new ArrayList<>();
|
||||||
// List<String> added = new ArrayList<>();
|
|
||||||
//
|
for (String defaultValueKey : defaultLang.getKeys(true)) {
|
||||||
// for (String defaultValueKey : defaultLang.getKeys(true)) {
|
Object val = defaultLang.get(defaultValueKey);
|
||||||
// Object val = defaultLang.get(defaultValueKey);
|
if (val instanceof ConfigSection) {
|
||||||
// if (val instanceof ConfigSection) {
|
continue;
|
||||||
// continue;
|
}
|
||||||
// }
|
|
||||||
//
|
if (!existingLang.contains(defaultValueKey)) {
|
||||||
// if (!existingLang.contains(defaultValueKey)) {
|
added.add(defaultValueKey);
|
||||||
// added.add(defaultValueKey);
|
existingLang.set(defaultValueKey, val);
|
||||||
// existingLang.set(defaultValueKey, val);
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
if (!added.isEmpty()) {
|
||||||
// if (!added.isEmpty()) {
|
if (!builtin) {
|
||||||
// if (!builtin) {
|
existingLang.setHeader("New messages added for " + plugin.getName() + " v" + plugin.getDescription().getVersion() + ".",
|
||||||
// existingLang.setHeader("New messages added for " + plugin.getName() + " v" + plugin.getDescription().getVersion() + ".",
|
"",
|
||||||
// "",
|
"These translations were found untranslated, join",
|
||||||
// "These translations were found untranslated, join",
|
"our translation Discord https://discord.gg/f7fpZEf",
|
||||||
// "our translation Discord https://discord.gg/f7fpZEf",
|
"to request an official update!",
|
||||||
// "to request an official update!",
|
"",
|
||||||
// "",
|
String.join("\n", added)
|
||||||
// String.join("\n", added)
|
);
|
||||||
// );
|
} else {
|
||||||
// } else {
|
existingLang.setHeader("New messages added for " + plugin.getName() + " v" + plugin.getDescription().getVersion() + ".",
|
||||||
// existingLang.setHeader("New messages added for " + plugin.getName() + " v" + plugin.getDescription().getVersion() + ".",
|
"",
|
||||||
// "",
|
String.join("\n", added)
|
||||||
// String.join("\n", added)
|
);
|
||||||
// );
|
}
|
||||||
// }
|
|
||||||
//
|
existingLang.setRootNodeSpacing(0);
|
||||||
// existingLang.setRootNodeSpacing(0);
|
existingLang.save();
|
||||||
// existingLang.save();
|
}
|
||||||
// }
|
|
||||||
//
|
existingLang.setRootNodeSpacing(0);
|
||||||
// existingLang.setRootNodeSpacing(0);
|
existingLang.save();
|
||||||
// existingLang.save();
|
|
||||||
//
|
return !added.isEmpty();
|
||||||
// return !added.isEmpty();
|
} catch (InvalidConfigurationException ex) {
|
||||||
// } catch (InvalidConfigurationException ex) {
|
plugin.getLogger().log(Level.SEVERE, "Error checking config " + existingFile.getName(), ex);
|
||||||
// plugin.getLogger().log(Level.SEVERE, "Error checking config " + existingFile.getName(), ex);
|
}
|
||||||
// }
|
} catch (IOException ignore) {
|
||||||
// } catch (IOException ignore) {
|
}
|
||||||
// }
|
|
||||||
//
|
return false;
|
||||||
// return false;
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Clear the previous message cache and load new messages directly from file
|
||||||
// * Clear the previous message cache and load new messages directly from file
|
*
|
||||||
// *
|
* @return reload messages from file
|
||||||
// * @return reload messages from file
|
*/
|
||||||
// */
|
public boolean reloadMessages() {
|
||||||
// public boolean reloadMessages() {
|
if (!this.file.exists()) {
|
||||||
// if (!this.file.exists()) {
|
plugin.getLogger().warning("Could not find file for locale \"" + this.name + "\"");
|
||||||
// plugin.getLogger().warning("Could not find file for locale \"" + this.name + "\"");
|
return false;
|
||||||
// return false;
|
}
|
||||||
// }
|
|
||||||
//
|
this.nodes.clear(); // Clear previous data (if any)
|
||||||
// this.nodes.clear(); // Clear previous data (if any)
|
|
||||||
//
|
// guess what encoding this file is in
|
||||||
// // guess what encoding this file is in
|
Charset charset = TextUtils.detectCharset(file, null);
|
||||||
// Charset charset = TextUtils.detectCharset(file, null);
|
if (charset == null) {
|
||||||
// if (charset == null) {
|
plugin.getLogger().warning("Could not determine charset for locale \"" + this.name + "\"");
|
||||||
// plugin.getLogger().warning("Could not determine charset for locale \"" + this.name + "\"");
|
charset = StandardCharsets.UTF_8;
|
||||||
// charset = StandardCharsets.UTF_8;
|
}
|
||||||
// }
|
|
||||||
//
|
// load in the file!
|
||||||
// // load in the file!
|
try (FileInputStream stream = new FileInputStream(file);
|
||||||
// try (FileInputStream stream = new FileInputStream(file);
|
BufferedReader source = new BufferedReader(new InputStreamReader(stream, charset));
|
||||||
// BufferedReader source = new BufferedReader(new InputStreamReader(stream, charset));
|
BufferedReader reader = translatePropertyToYAML(source, charset)) {
|
||||||
// BufferedReader reader = translatePropertyToYAML(source, charset)) {
|
Config lang = new Config(file);
|
||||||
// Config lang = new Config(file);
|
lang.load(reader);
|
||||||
// lang.load(reader);
|
translateMsgRoot(lang, file, charset);
|
||||||
// translateMsgRoot(lang, file, charset);
|
|
||||||
//
|
// load lists as strings with newlines
|
||||||
// // load lists as strings with newlines
|
lang.getValues(true).forEach((k, v) -> nodes.put(k,
|
||||||
// lang.getValues(true).forEach((k, v) -> nodes.put(k,
|
v instanceof List
|
||||||
// v instanceof List
|
? (((List<?>) v).stream().map(Object::toString).collect(Collectors.joining("\n")))
|
||||||
// ? (((List<?>) v).stream().map(Object::toString).collect(Collectors.joining("\n")))
|
: v.toString()));
|
||||||
// : v.toString()));
|
|
||||||
//
|
return true;
|
||||||
// return true;
|
} catch (IOException ex) {
|
||||||
// } catch (IOException ex) {
|
ex.printStackTrace();
|
||||||
// ex.printStackTrace();
|
} catch (InvalidConfigurationException ex) {
|
||||||
// } catch (InvalidConfigurationException ex) {
|
Logger.getLogger(Locale.class.getName()).log(Level.SEVERE, "Configuration error in language file \"" + file.getName() + "\"", ex);
|
||||||
// Logger.getLogger(Locale.class.getName()).log(Level.SEVERE, "Configuration error in language file \"" + file.getName() + "\"", ex);
|
}
|
||||||
// }
|
|
||||||
//
|
return false;
|
||||||
// return false;
|
}
|
||||||
// }
|
|
||||||
//
|
protected static BufferedReader translatePropertyToYAML(BufferedReader source, Charset charset) throws IOException {
|
||||||
// protected static BufferedReader translatePropertyToYAML(BufferedReader source, Charset charset) throws IOException {
|
StringBuilder output = new StringBuilder();
|
||||||
// StringBuilder output = new StringBuilder();
|
|
||||||
//
|
String line, line1;
|
||||||
// String line, line1;
|
for (int lineNumber = 0; (line = source.readLine()) != null; ++lineNumber) {
|
||||||
// for (int lineNumber = 0; (line = source.readLine()) != null; ++lineNumber) {
|
if (lineNumber == 0) {
|
||||||
// if (lineNumber == 0) {
|
// remove BOM markers, if any
|
||||||
// // remove BOM markers, if any
|
line1 = line;
|
||||||
// line1 = line;
|
line = line.replaceAll("[\uFEFF\uFFFE\u200B]", "");
|
||||||
// line = line.replaceAll("[\uFEFF\uFFFE\u200B]", "");
|
if (line1.length() != line.length()) {
|
||||||
// if (line1.length() != line.length()) {
|
output.append(line1, 0, line1.length() - line.length());
|
||||||
// output.append(line1, 0, line1.length() - line.length());
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
Matcher matcher;
|
||||||
// Matcher matcher;
|
if ((line = line.replace('\r', ' ')
|
||||||
// if ((line = line.replace('\r', ' ')
|
.replaceAll("\\p{C}", "?")
|
||||||
// .replaceAll("\\p{C}", "?")
|
.replace(";", "")).trim().isEmpty()
|
||||||
// .replace(";", "")).trim().isEmpty()
|
|| line.trim().startsWith("#") /* Comment */
|
||||||
// || line.trim().startsWith("#") /* Comment */
|
// need to trim the search group because tab characters somehow ended up at the end of lines in a lot of these files
|
||||||
// // need to trim the search group because tab characters somehow ended up at the end of lines in a lot of these files
|
|| !(matcher = OLD_NODE_PATTERN.matcher(line.trim())).find()) {
|
||||||
// || !(matcher = OLD_NODE_PATTERN.matcher(line.trim())).find()) {
|
if (line.startsWith("//")) {
|
||||||
// if (line.startsWith("//")) {
|
// someone used an improper comment in some files *grumble grumble*
|
||||||
// // someone used an improper comment in some files *grumble grumble*
|
output.append("#").append(line).append("\n");
|
||||||
// output.append("#").append(line).append("\n");
|
} else {
|
||||||
// } else {
|
output.append(line).append("\n");
|
||||||
// output.append(line).append("\n");
|
}
|
||||||
// }
|
} else {
|
||||||
// } else {
|
output.append(matcher.group(1)).append(": \"").append(matcher.group(2)).append("\"\n");
|
||||||
// output.append(matcher.group(1)).append(": \"").append(matcher.group(2)).append("\"\n");
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
// I hate Java sometimes because of crap like this:
|
||||||
// // I hate Java sometimes because of crap like this:
|
return new BufferedReader(new InputStreamReader(new BufferedInputStream(new ByteArrayInputStream(output.toString().getBytes(charset))), charset));
|
||||||
// return new BufferedReader(new InputStreamReader(new BufferedInputStream(new ByteArrayInputStream(output.toString().getBytes(charset))), charset));
|
}
|
||||||
// }
|
|
||||||
//
|
protected static void translateMsgRoot(Config lang, File file, Charset charset) throws IOException {
|
||||||
// protected static void translateMsgRoot(Config lang, File file, Charset charset) throws IOException {
|
List<String> msgs = lang.getValues(true).entrySet().stream()
|
||||||
// List<String> msgs = lang.getValues(true).entrySet().stream()
|
.filter(e -> e.getValue() instanceof ConfigSection)
|
||||||
// .filter(e -> e.getValue() instanceof ConfigSection)
|
.map(Map.Entry::getKey)
|
||||||
// .map(Map.Entry::getKey)
|
.collect(Collectors.toList());
|
||||||
// .collect(Collectors.toList());
|
|
||||||
//
|
if (!msgs.isEmpty()) {
|
||||||
// if (!msgs.isEmpty()) {
|
try (FileInputStream stream = new FileInputStream(file);
|
||||||
// try (FileInputStream stream = new FileInputStream(file);
|
BufferedReader source = new BufferedReader(new InputStreamReader(stream, charset))) {
|
||||||
// BufferedReader source = new BufferedReader(new InputStreamReader(stream, charset))) {
|
String line;
|
||||||
// String line;
|
for (int lineNumber = 0; (line = source.readLine()) != null; ++lineNumber) {
|
||||||
// for (int lineNumber = 0; (line = source.readLine()) != null; ++lineNumber) {
|
if (lineNumber == 0) {
|
||||||
// if (lineNumber == 0) {
|
// remove BOM markers, if any
|
||||||
// // remove BOM markers, if any
|
line = line.replaceAll("[\uFEFF\uFFFE\u200B]", "");
|
||||||
// line = line.replaceAll("[\uFEFF\uFFFE\u200B]", "");
|
}
|
||||||
// }
|
|
||||||
//
|
Matcher matcher;
|
||||||
// Matcher matcher;
|
if (!(line = line.trim()).isEmpty() && !line.startsWith("#")
|
||||||
// if (!(line = line.trim()).isEmpty() && !line.startsWith("#")
|
&& (matcher = OLD_NODE_PATTERN.matcher(line)).find()
|
||||||
// && (matcher = OLD_NODE_PATTERN.matcher(line)).find()
|
&& msgs.contains(matcher.group(1))) {
|
||||||
// && msgs.contains(matcher.group(1))) {
|
lang.set(matcher.group(1) + ".message", matcher.group(2));
|
||||||
// lang.set(matcher.group(1) + ".message", matcher.group(2));
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
protected static void translateMsgRoot(Config lang, String file, Charset charset) throws IOException {
|
||||||
// protected static void translateMsgRoot(Config lang, String file, Charset charset) throws IOException {
|
List<String> msgs = lang.getValues(true).entrySet().stream()
|
||||||
// List<String> msgs = lang.getValues(true).entrySet().stream()
|
.filter(e -> e.getValue() instanceof ConfigSection)
|
||||||
// .filter(e -> e.getValue() instanceof ConfigSection)
|
.map(Map.Entry::getKey)
|
||||||
// .map(Map.Entry::getKey)
|
.collect(Collectors.toList());
|
||||||
// .collect(Collectors.toList());
|
|
||||||
//
|
if (!msgs.isEmpty()) {
|
||||||
// if (!msgs.isEmpty()) {
|
String[] source = file.split("\n");
|
||||||
// String[] source = file.split("\n");
|
|
||||||
//
|
String line;
|
||||||
// String line;
|
for (int lineNumber = 0; lineNumber < source.length; ++lineNumber) {
|
||||||
// for (int lineNumber = 0; lineNumber < source.length; ++lineNumber) {
|
line = source[lineNumber];
|
||||||
// line = source[lineNumber];
|
if (lineNumber == 0) {
|
||||||
// if (lineNumber == 0) {
|
// remove BOM markers, if any
|
||||||
// // remove BOM markers, if any
|
line = line.replaceAll("[\uFEFF\uFFFE\u200B]", "");
|
||||||
// line = line.replaceAll("[\uFEFF\uFFFE\u200B]", "");
|
}
|
||||||
// }
|
|
||||||
//
|
Matcher matcher;
|
||||||
// Matcher matcher;
|
if (!(line = line.trim()).isEmpty() && !line.startsWith("#")
|
||||||
// if (!(line = line.trim()).isEmpty() && !line.startsWith("#")
|
&& (matcher = OLD_NODE_PATTERN.matcher(line)).find()
|
||||||
// && (matcher = OLD_NODE_PATTERN.matcher(line)).find()
|
&& msgs.contains(matcher.group(1))) {
|
||||||
// && msgs.contains(matcher.group(1))) {
|
lang.set(matcher.group(1) + ".message", matcher.group(2));
|
||||||
// lang.set(matcher.group(1) + ".message", matcher.group(2));
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Supply the Message object with the plugins prefix.
|
||||||
// * Supply the Message object with the plugins prefix.
|
*
|
||||||
// *
|
* @param message message to be applied
|
||||||
// * @param message message to be applied
|
*
|
||||||
// *
|
* @return applied message
|
||||||
// * @return applied message
|
*/
|
||||||
// */
|
private Message supplyPrefix(Message message) {
|
||||||
// private Message supplyPrefix(Message message) {
|
return message.setPrefix(this.nodes.getOrDefault("general.nametag.prefix", "[" + plugin.getName() + "]"));
|
||||||
// return message.setPrefix(this.nodes.getOrDefault("general.nametag.prefix", "[" + plugin.getName() + "]"));
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Create a new unsaved Message
|
||||||
// * Create a new unsaved Message
|
*
|
||||||
// *
|
* @param message the message to create
|
||||||
// * @param message the message to create
|
*
|
||||||
// *
|
* @return the created message
|
||||||
// * @return the created message
|
*/
|
||||||
// */
|
public Message newMessage(String message) {
|
||||||
// public Message newMessage(String message) {
|
return supplyPrefix(new Message(message));
|
||||||
// return supplyPrefix(new Message(message));
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Get a message set for a specific node.
|
||||||
// * Get a message set for a specific node.
|
*
|
||||||
// *
|
* @param node the node to get
|
||||||
// * @param node the node to get
|
*
|
||||||
// *
|
* @return the message for the specified node
|
||||||
// * @return the message for the specified node
|
*/
|
||||||
// */
|
public Message getMessage(String node) {
|
||||||
// public Message getMessage(String node) {
|
if (this.nodes.containsKey(node + ".message")) {
|
||||||
// if (this.nodes.containsKey(node + ".message")) {
|
node += ".message";
|
||||||
// node += ".message";
|
}
|
||||||
// }
|
|
||||||
//
|
return this.getMessageOrDefault(node, node);
|
||||||
// return this.getMessageOrDefault(node, node);
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Get a message set for a specific node
|
||||||
// * Get a message set for a specific node
|
*
|
||||||
// *
|
* @param node the node to get
|
||||||
// * @param node the node to get
|
* @param defaultValue the default value given that a value for the node was not found
|
||||||
// * @param defaultValue the default value given that a value for the node was not found
|
*
|
||||||
// *
|
* @return the message for the specified node. Default if none found
|
||||||
// * @return the message for the specified node. Default if none found
|
*/
|
||||||
// */
|
public Message getMessageOrDefault(String node, String defaultValue) {
|
||||||
// public Message getMessageOrDefault(String node, String defaultValue) {
|
if (this.nodes.containsKey(node + ".message")) {
|
||||||
// if (this.nodes.containsKey(node + ".message")) {
|
node += ".message";
|
||||||
// node += ".message";
|
}
|
||||||
// }
|
|
||||||
//
|
return supplyPrefix(new Message(this.nodes.getOrDefault(node, defaultValue)));
|
||||||
// return supplyPrefix(new Message(this.nodes.getOrDefault(node, defaultValue)));
|
}
|
||||||
// }
|
|
||||||
//
|
/**
|
||||||
// /**
|
* Return the locale name (i.e. "en_US")
|
||||||
// * Return the locale name (i.e. "en_US")
|
*
|
||||||
// *
|
* @return the locale name
|
||||||
// * @return the locale name
|
*/
|
||||||
// */
|
public String getName() {
|
||||||
// public String getName() {
|
return name;
|
||||||
// return name;
|
}
|
||||||
// }
|
|
||||||
//
|
private static void copy(InputStream input, OutputStream output) {
|
||||||
// private static void copy(InputStream input, OutputStream output) {
|
int n;
|
||||||
// int n;
|
byte[] buffer = new byte[1024 * 4];
|
||||||
// byte[] buffer = new byte[1024 * 4];
|
|
||||||
//
|
try {
|
||||||
// try {
|
while ((n = input.read(buffer)) != -1) {
|
||||||
// while ((n = input.read(buffer)) != -1) {
|
output.write(buffer, 0, n);
|
||||||
// output.write(buffer, 0, n);
|
}
|
||||||
// }
|
} catch (IOException ex) {
|
||||||
// } catch (IOException ex) {
|
ex.printStackTrace();
|
||||||
// ex.printStackTrace();
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//}
|
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
package com.songoda.core.locale;
|
|
||||||
|
|
||||||
import com.songoda.core.http.HttpClient;
|
|
||||||
import com.songoda.core.http.HttpResponse;
|
|
||||||
import com.songoda.core.http.UnexpectedHttpStatusException;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class LocaleFileManager {
|
|
||||||
private final HttpClient httpClient;
|
|
||||||
private final String projectName;
|
|
||||||
|
|
||||||
public LocaleFileManager(HttpClient httpClient, String projectName) {
|
|
||||||
this.httpClient = httpClient;
|
|
||||||
this.projectName = projectName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> downloadMissingTranslations(File targetDirectory) throws IOException {
|
|
||||||
List<String> availableLanguages = this.fetchAvailableLanguageFiles();
|
|
||||||
if (availableLanguages == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Files.createDirectories(targetDirectory.toPath());
|
|
||||||
|
|
||||||
List<String> downloadedLocales = new LinkedList<>();
|
|
||||||
for (String languageFileName : availableLanguages) {
|
|
||||||
File languageFile = new File(targetDirectory, languageFileName);
|
|
||||||
if (languageFile.exists()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String languageFileContents = fetchProjectFile(languageFileName);
|
|
||||||
if (languageFileContents == null) {
|
|
||||||
throw new IOException("Failed to download language file " + languageFileName); // TODO: Better exception
|
|
||||||
}
|
|
||||||
|
|
||||||
try (Writer writer = Files.newBufferedWriter(languageFile.toPath(), StandardCharsets.UTF_8)) {
|
|
||||||
writer.write(languageFileContents);
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadedLocales.add(languageFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return downloadedLocales;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable List<String> fetchAvailableLanguageFiles() throws IOException {
|
|
||||||
String projectLanguageIndex = fetchProjectFile("_index.txt");
|
|
||||||
|
|
||||||
if (projectLanguageIndex == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> result = new LinkedList<>();
|
|
||||||
|
|
||||||
for (String line : projectLanguageIndex.split("\r?\n")) {
|
|
||||||
line = line.trim();
|
|
||||||
|
|
||||||
if (!line.startsWith("#") && !line.isEmpty()) {
|
|
||||||
result.add(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String fetchProjectFile(String fileName) throws IOException {
|
|
||||||
String url = formatUrl("https://songoda.github.io/Translations/projects/%s/%s", this.projectName, fileName);
|
|
||||||
HttpResponse httpResponse = this.httpClient.get(url);
|
|
||||||
|
|
||||||
if (httpResponse.getResponseCode() == 404) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (httpResponse.getResponseCode() != 200) {
|
|
||||||
throw new UnexpectedHttpStatusException(httpResponse.getResponseCode(), url);
|
|
||||||
}
|
|
||||||
|
|
||||||
return httpResponse.getBodyAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatUrl(String url, Object... params) throws UnsupportedEncodingException {
|
|
||||||
Object[] encodedParams = new Object[params.length];
|
|
||||||
for (int i = 0; i < params.length; i++) {
|
|
||||||
encodedParams[i] = URLEncoder.encode(params[i].toString(), "UTF-8");
|
|
||||||
}
|
|
||||||
|
|
||||||
return String.format(url, encodedParams);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
package com.songoda.core.locale;
|
|
||||||
|
|
||||||
import com.songoda.core.configuration.songoda.SongodaYamlConfig;
|
|
||||||
import com.songoda.core.configuration.yaml.YamlConfiguration;
|
|
||||||
import com.songoda.core.http.SimpleHttpClient;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class LocaleManager {
|
|
||||||
protected final Plugin plugin;
|
|
||||||
protected final File localesDirectory;
|
|
||||||
|
|
||||||
protected final List<SongodaYamlConfig> loadedLocales = new LinkedList<>();
|
|
||||||
protected final @Nullable YamlConfiguration fallbackLocale;
|
|
||||||
|
|
||||||
public LocaleManager(Plugin plugin) throws IOException {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.localesDirectory = new File(this.plugin.getDataFolder(), "locales");
|
|
||||||
|
|
||||||
this.fallbackLocale = loadFallbackLocale();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> downloadMissingLocales() {
|
|
||||||
LocaleFileManager localeFileManager = new LocaleFileManager(new SimpleHttpClient(), this.plugin.getName());
|
|
||||||
|
|
||||||
try {
|
|
||||||
return localeFileManager.downloadMissingTranslations(this.localesDirectory);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
this.plugin.getLogger().warning("Failed to download missing locales: " + ex.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load(String locale) throws IOException {
|
|
||||||
File fileToLoad = determineAvailableLocaleVariation(locale);
|
|
||||||
if (fileToLoad == null) {
|
|
||||||
throw new FileNotFoundException("Locale file " + locale + " not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (SongodaYamlConfig loadedLocale : this.loadedLocales) {
|
|
||||||
if (loadedLocale.file.equals(fileToLoad)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SongodaYamlConfig localeConfig = new SongodaYamlConfig(fileToLoad);
|
|
||||||
localeConfig.load();
|
|
||||||
this.loadedLocales.add(localeConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadExclusively(String locale) {
|
|
||||||
loadExclusively(Collections.singletonList(locale));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadExclusively(List<String> locales) {
|
|
||||||
unloadAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unloadAll() {
|
|
||||||
this.loadedLocales.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected @Nullable File determineAvailableLocaleVariation(String locale) {
|
|
||||||
File localeFile = new File(this.localesDirectory, locale + ".lang");
|
|
||||||
if (localeFile.exists()) {
|
|
||||||
return localeFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
File[] availableLocales = this.localesDirectory.listFiles();
|
|
||||||
if (availableLocales == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (File availableLocale : availableLocales) {
|
|
||||||
if (availableLocale.getName().startsWith(locale)) {
|
|
||||||
return availableLocale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected @Nullable YamlConfiguration loadFallbackLocale() throws IOException {
|
|
||||||
URL fallbackLocaleUrl = this.plugin.getClass().getResource("/en_US.lang");
|
|
||||||
if (fallbackLocaleUrl == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
YamlConfiguration locale = new YamlConfiguration();
|
|
||||||
try (Reader reader = new InputStreamReader(fallbackLocaleUrl.openStream(), StandardCharsets.UTF_8)) {
|
|
||||||
locale.load(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SongodaYamlConfig parseLocaleFile(File file) throws IOException {
|
|
||||||
SongodaYamlConfig locale = new SongodaYamlConfig(file, this.plugin.getLogger());
|
|
||||||
locale.load();
|
|
||||||
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package com.songoda.core.utils;
|
|
||||||
|
|
||||||
public class Pair<T, U> {
|
|
||||||
private final T first;
|
|
||||||
private final U second;
|
|
||||||
|
|
||||||
public Pair(T first, U second) {
|
|
||||||
this.first = first;
|
|
||||||
this.second = second;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getFirst() {
|
|
||||||
return this.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
public U getSecond() {
|
|
||||||
return this.second;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package com.songoda.core.configuration;
|
|
||||||
|
|
||||||
import com.songoda.core.configuration.yaml.YamlConfiguration;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
public class ReadOnlyConfigEntryTest {
|
|
||||||
@Test
|
|
||||||
void testGetKey() {
|
|
||||||
ConfigEntry entry = new ReadOnlyConfigEntry(new YamlConfiguration(), "key-1");
|
|
||||||
|
|
||||||
assertEquals("key-1", entry.getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetConfig() {
|
|
||||||
IConfiguration config = new YamlConfiguration();
|
|
||||||
ConfigEntry entry = new ReadOnlyConfigEntry(config, "key");
|
|
||||||
|
|
||||||
assertSame(config, entry.getConfig());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNullGetters() {
|
|
||||||
ConfigEntry entry = new ReadOnlyConfigEntry(new YamlConfiguration(), "key-null");
|
|
||||||
|
|
||||||
assertNull(entry.getDefaultValue());
|
|
||||||
assertNull(entry.getUpgradeSteps());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testWritingMethodsDoingNothing() {
|
|
||||||
YamlConfiguration config = new YamlConfiguration();
|
|
||||||
ConfigEntry entry = new ReadOnlyConfigEntry(config, "key");
|
|
||||||
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> entry.setDefaultValue("value"));
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> entry.withDefaultValue("value"));
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> entry.withComment("A comment."));
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> entry.withUpgradeStep(0, "old-key"));
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> entry.set("value"));
|
|
||||||
|
|
||||||
assertNull(entry.getDefaultValue());
|
|
||||||
assertNull(entry.getUpgradeSteps());
|
|
||||||
assertNull(config.getNodeComment("key"));
|
|
||||||
assertNull(entry.getUpgradeSteps());
|
|
||||||
assertNull(entry.get());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
package com.songoda.core.configuration.songoda;
|
|
||||||
|
|
||||||
import com.songoda.core.configuration.ConfigEntry;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class SongodaYamlConfigRoundtripTest {
|
|
||||||
private Path testDirectoryPath;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() throws IOException {
|
|
||||||
this.testDirectoryPath = Files.createTempDirectory("SongodaCore-YamlConfigRoundtripTest");
|
|
||||||
this.testDirectoryPath.toFile().deleteOnExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
void tearDown() throws IOException {
|
|
||||||
try (Stream<Path> paths = Files.list(this.testDirectoryPath)) {
|
|
||||||
for (Path path : paths.toArray(Path[]::new)) {
|
|
||||||
Files.deleteIfExists(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Files.deleteIfExists(this.testDirectoryPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void roundtripTest() throws IOException {
|
|
||||||
Path testFilePath = this.testDirectoryPath.resolve("config.yml");
|
|
||||||
|
|
||||||
Files.write(testFilePath, ("# Don't touch this – it's used to track the version of the config.\n" +
|
|
||||||
"version: 1\n" +
|
|
||||||
"messages:\n" +
|
|
||||||
" # This message is shown when the 'foo' command succeeds.\n" +
|
|
||||||
" fooSuccess: Remastered success value\n" +
|
|
||||||
"# This is the range of the 'foo' command\n").getBytes());
|
|
||||||
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(testFilePath.toFile())
|
|
||||||
.withVersion(3);
|
|
||||||
|
|
||||||
ConfigEntry cmdFooSuccess = cfg.createEntry("command.foo.success")
|
|
||||||
.withDefaultValue("Default success value")
|
|
||||||
.withComment("This message is shown when the 'foo' command succeeds.")
|
|
||||||
.withUpgradeStep(1, "messages.fooSuccess");
|
|
||||||
ConfigEntry range = cfg.createEntry("range")
|
|
||||||
.withComment("This is the range of the 'foo' command")
|
|
||||||
.withUpgradeStep(1, null, o -> {
|
|
||||||
if (o == null) {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
return o;
|
|
||||||
})
|
|
||||||
.withUpgradeStep(2, null, o -> o + " blocks");
|
|
||||||
ConfigEntry incrementer = cfg.createEntry("incrementer", 0)
|
|
||||||
.withComment("This is the incrementer of the 'foo' command")
|
|
||||||
.withUpgradeStep(1, null, o -> {
|
|
||||||
if (o == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) o + 1;
|
|
||||||
})
|
|
||||||
.withUpgradeStep(3, null, (o) -> "text");
|
|
||||||
ConfigEntry entryWithoutUpgradeStep = cfg.createEntry("entryWithoutUpgradeStep")
|
|
||||||
.withDefaultValue("Default value")
|
|
||||||
.withComment("This is the entry without an upgrade step");
|
|
||||||
|
|
||||||
ConfigEntry entryWithCyrillic = cfg.createEntry("entryWithCyrillic")
|
|
||||||
.withDefaultValue("Кириллица")
|
|
||||||
.withComment("This is the entry with cyrillic characters");
|
|
||||||
|
|
||||||
assertTrue(cfg.init());
|
|
||||||
|
|
||||||
assertNull(cfg.get("messages.fooSuccess"));
|
|
||||||
assertEquals("Remastered success value", cfg.get("command.foo.success"));
|
|
||||||
assertEquals("Remastered success value", cmdFooSuccess.get());
|
|
||||||
assertTrue(cmdFooSuccess.has());
|
|
||||||
|
|
||||||
assertTrue(range.has());
|
|
||||||
assertEquals(cfg.get("range"), range.get());
|
|
||||||
|
|
||||||
assertTrue(incrementer.has());
|
|
||||||
assertEquals(cfg.get("incrementer"), incrementer.get());
|
|
||||||
|
|
||||||
assertTrue(entryWithoutUpgradeStep.has());
|
|
||||||
assertEquals(cfg.get("entryWithoutUpgradeStep"), entryWithoutUpgradeStep.get());
|
|
||||||
|
|
||||||
assertTrue(entryWithCyrillic.has());
|
|
||||||
assertEquals(cfg.get("entryWithCyrillic"), entryWithCyrillic.get());
|
|
||||||
|
|
||||||
assertEquals("# Don't touch this – it's used to track the version of the config.\n" +
|
|
||||||
"version: 3\n" +
|
|
||||||
"command:\n" +
|
|
||||||
" foo:\n" +
|
|
||||||
" # This message is shown when the 'foo' command succeeds.\n" +
|
|
||||||
" success: Remastered success value\n" +
|
|
||||||
"# This is the range of the 'foo' command\n" +
|
|
||||||
"range: 10 blocks\n" +
|
|
||||||
"# This is the incrementer of the 'foo' command\n" +
|
|
||||||
"incrementer: 0\n" +
|
|
||||||
"# This is the entry without an upgrade step\n" +
|
|
||||||
"entryWithoutUpgradeStep: Default value\n" +
|
|
||||||
"# This is the entry with cyrillic characters\n" +
|
|
||||||
"entryWithCyrillic: Кириллица\n", new String(Files.readAllBytes(testFilePath)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,246 +0,0 @@
|
||||||
package com.songoda.core.configuration.songoda;
|
|
||||||
|
|
||||||
import com.songoda.core.configuration.ConfigEntry;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class SongodaYamlConfigTest {
|
|
||||||
Path tmpDir;
|
|
||||||
Path cfg;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() throws IOException {
|
|
||||||
this.tmpDir = Files.createTempDirectory("SongodaYamlConfigTest");
|
|
||||||
|
|
||||||
this.cfg = Files.createTempFile(this.tmpDir, "config", ".yml");
|
|
||||||
this.tmpDir.toFile().deleteOnExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
void tearDown() throws IOException {
|
|
||||||
try (Stream<Path> stream = Files.walk(this.tmpDir)) {
|
|
||||||
stream
|
|
||||||
.sorted(Comparator.reverseOrder())
|
|
||||||
.map(Path::toFile)
|
|
||||||
.forEach(File::delete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testLoad() throws IOException {
|
|
||||||
Files.write(this.cfg, "test-key: foo\n".getBytes());
|
|
||||||
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
|
|
||||||
cfg.set("test-key", "bar");
|
|
||||||
cfg.load();
|
|
||||||
|
|
||||||
assertEquals("foo", cfg.get("test-key"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSave() throws IOException {
|
|
||||||
Files.write(this.cfg, "test-key: foo\n".getBytes());
|
|
||||||
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
|
|
||||||
cfg.set("test-key", "bar");
|
|
||||||
cfg.save();
|
|
||||||
|
|
||||||
assertEquals("test-key: bar\n", new String(Files.readAllBytes(this.cfg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSaveToNonExistingSubDirectory() throws IOException {
|
|
||||||
File configFile = new File(this.tmpDir.toFile(), "testSaveToNonExistingSubDirectory/config.yml");
|
|
||||||
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(configFile);
|
|
||||||
cfg.set("test-key", "bar");
|
|
||||||
cfg.save();
|
|
||||||
|
|
||||||
assertEquals("test-key: bar\n", new String(Files.readAllBytes(configFile.toPath())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testWithVersion() throws IOException {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
|
|
||||||
cfg.withVersion("version-key", 1, null);
|
|
||||||
|
|
||||||
assertEquals(1, cfg.get("version-key"));
|
|
||||||
|
|
||||||
cfg.save();
|
|
||||||
assertEquals("version-key: 1\n", new String(Files.readAllBytes(this.cfg)));
|
|
||||||
|
|
||||||
cfg.withVersion(2);
|
|
||||||
|
|
||||||
assertEquals(2, cfg.get("version"));
|
|
||||||
assertNull(cfg.get("version-key"));
|
|
||||||
|
|
||||||
cfg.save();
|
|
||||||
assertEquals(
|
|
||||||
"# Don't touch this – it's used to track the version of the config.\n" +
|
|
||||||
"version: 2\n",
|
|
||||||
new String(Files.readAllBytes(this.cfg))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testWithZeroVersion() throws IOException {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
|
|
||||||
cfg.withVersion("version-key", 0, null);
|
|
||||||
|
|
||||||
assertEquals(0, cfg.get("version-key"));
|
|
||||||
|
|
||||||
cfg.save();
|
|
||||||
assertEquals("version-key: 0\n", new String(Files.readAllBytes(this.cfg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testWithNegativeVersion() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> cfg.withVersion("version-key", -1, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testLoadWithTooNewVersion() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile())
|
|
||||||
.withVersion(1);
|
|
||||||
|
|
||||||
assertThrows(IllegalStateException.class, () -> cfg.load(new StringReader("version: 10\n")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testWithUpToDateVersion() throws IOException {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile())
|
|
||||||
.withVersion(2);
|
|
||||||
|
|
||||||
assertFalse(cfg.upgradeOldConfigVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testWithNewerVersion() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile())
|
|
||||||
.withVersion(5);
|
|
||||||
|
|
||||||
assertThrows(IllegalStateException.class, cfg::upgradeOldConfigVersionByOne);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testWithKeyWithoutConfigEntry() throws IOException {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
|
|
||||||
|
|
||||||
cfg.set("test-key", "foo");
|
|
||||||
cfg.load();
|
|
||||||
|
|
||||||
assertNull(cfg.get("test-key"));
|
|
||||||
|
|
||||||
cfg.set("test-key", "foo");
|
|
||||||
assertEquals("foo", cfg.get("test-key"));
|
|
||||||
|
|
||||||
cfg.save();
|
|
||||||
cfg.load();
|
|
||||||
|
|
||||||
assertEquals("foo", cfg.get("test-key"));
|
|
||||||
assertEquals(1, cfg.getKeys("").size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCreateEntryAppliesDefaultValueForNullValue() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", "value");
|
|
||||||
|
|
||||||
cfg.init();
|
|
||||||
|
|
||||||
assertEquals("value", entry.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCreateDuplicateEntry() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", null);
|
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> cfg.createEntry("key", "other-value"));
|
|
||||||
|
|
||||||
assertNull(entry.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testVersionUpgradePersistsCommentsOnKeyChange() throws IOException {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile())
|
|
||||||
.withVersion(2);
|
|
||||||
|
|
||||||
cfg.createEntry("newKey", "value")
|
|
||||||
.withComment("This is a comment")
|
|
||||||
.withUpgradeStep(1, "oldKey");
|
|
||||||
|
|
||||||
cfg.load(new StringReader("version: 1\noldKey: old-value\n"));
|
|
||||||
|
|
||||||
assertNull(cfg.get("oldKey"));
|
|
||||||
assertNull(cfg.getNodeComment("oldKey"));
|
|
||||||
|
|
||||||
assertEquals("old-value", cfg.get("newKey"));
|
|
||||||
assertEquals("This is a comment", Objects.requireNonNull(cfg.getNodeComment("newKey")).get());
|
|
||||||
|
|
||||||
StringWriter writer = new StringWriter();
|
|
||||||
cfg.save(writer);
|
|
||||||
|
|
||||||
assertEquals("# Don't touch this – it's used to track the version of the config.\n" +
|
|
||||||
"version: 2\n" +
|
|
||||||
"# This is a comment\n" +
|
|
||||||
"newKey: old-value\n",
|
|
||||||
writer.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testReadOnlyEntry() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", "default-value");
|
|
||||||
ConfigEntry readOnlyConfigEntry = cfg.getReadEntry("key");
|
|
||||||
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> readOnlyConfigEntry.set("new-value"));
|
|
||||||
assertEquals("default-value", entry.get());
|
|
||||||
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> readOnlyConfigEntry.setDefaultValue("new-default-value"));
|
|
||||||
assertEquals("default-value", entry.get());
|
|
||||||
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> readOnlyConfigEntry.withComment("test-comment"));
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> readOnlyConfigEntry.withComment(() -> "test-comment"));
|
|
||||||
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> readOnlyConfigEntry.withUpgradeStep(10, "new-key"));
|
|
||||||
assertThrows(UnsupportedOperationException.class, () -> readOnlyConfigEntry.withUpgradeStep(10, "new-key", (o) -> "new-value"));
|
|
||||||
|
|
||||||
assertEquals("default-value", entry.get());
|
|
||||||
|
|
||||||
entry.set("new-value");
|
|
||||||
assertEquals("new-value", readOnlyConfigEntry.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testInit_Failure() {
|
|
||||||
assertTrue(this.cfg.toFile().setWritable(false));
|
|
||||||
|
|
||||||
Logger mockLogger = Mockito.mock(Logger.class);
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile(), mockLogger);
|
|
||||||
|
|
||||||
cfg.createEntry("key", "default-value");
|
|
||||||
|
|
||||||
assertFalse(cfg.init());
|
|
||||||
Mockito.verify(mockLogger).log(Mockito.eq(Level.SEVERE), Mockito.anyString(), Mockito.any(IOException.class));
|
|
||||||
|
|
||||||
assertTrue(this.cfg.toFile().setWritable(true));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,258 +0,0 @@
|
||||||
package com.songoda.core.configuration.yaml;
|
|
||||||
|
|
||||||
import com.songoda.core.compatibility.CompatibleMaterial;
|
|
||||||
import com.songoda.core.configuration.ConfigEntry;
|
|
||||||
import com.songoda.core.configuration.songoda.SongodaYamlConfig;
|
|
||||||
import com.songoda.ultimatestacker.core.configuration.Config;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
class YamlConfigEntryTest {
|
|
||||||
@Test
|
|
||||||
void testHas() {
|
|
||||||
YamlConfiguration config = new YamlConfiguration();
|
|
||||||
config.set("key-2", "value-2");
|
|
||||||
|
|
||||||
ConfigEntry entry1 = new YamlConfigEntry(config, "key-1", null);
|
|
||||||
ConfigEntry entry2 = new YamlConfigEntry(config, "key-2", null);
|
|
||||||
|
|
||||||
assertFalse(entry1.has());
|
|
||||||
assertTrue(entry2.has());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetKey() {
|
|
||||||
ConfigEntry entry = new YamlConfigEntry(new YamlConfiguration(), "key-1", null);
|
|
||||||
assertEquals("key-1", entry.getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetConfig() {
|
|
||||||
YamlConfiguration config = new YamlConfiguration();
|
|
||||||
ConfigEntry entry = new YamlConfigEntry(config, "key-1", null);
|
|
||||||
assertSame(config, entry.getConfig());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetDefaultValue() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", "value");
|
|
||||||
|
|
||||||
assertEquals("value", entry.getDefaultValue());
|
|
||||||
|
|
||||||
entry.setDefaultValue("new-value");
|
|
||||||
assertEquals("new-value", entry.getDefaultValue());
|
|
||||||
|
|
||||||
entry.withDefaultValue("new-value-2");
|
|
||||||
assertEquals("new-value-2", entry.getDefaultValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetOr() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", "value");
|
|
||||||
|
|
||||||
assertEquals("value", entry.getOr("invalid"));
|
|
||||||
|
|
||||||
entry.set(null);
|
|
||||||
assertEquals("invalid", entry.getOr("invalid"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetString() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", null);
|
|
||||||
|
|
||||||
entry.set("value");
|
|
||||||
assertEquals("value", entry.getString());
|
|
||||||
|
|
||||||
entry.set("new-value");
|
|
||||||
assertEquals("new-value", entry.getString());
|
|
||||||
|
|
||||||
entry.set(null);
|
|
||||||
assertNull(entry.getString());
|
|
||||||
assertNull(entry.getStringOr(null));
|
|
||||||
assertEquals("12", entry.getStringOr("12"));
|
|
||||||
|
|
||||||
entry.set(10.5);
|
|
||||||
assertEquals("10.5", entry.getString());
|
|
||||||
|
|
||||||
entry.set(true);
|
|
||||||
assertEquals("true", entry.getString());
|
|
||||||
|
|
||||||
entry.set(CompatibleMaterial.STONE);
|
|
||||||
assertEquals("STONE", entry.getString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetInt() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", null);
|
|
||||||
|
|
||||||
entry.set(1.0);
|
|
||||||
assertEquals(1, entry.getInt());
|
|
||||||
|
|
||||||
entry.set("1.5");
|
|
||||||
assertEquals(1, entry.getInt());
|
|
||||||
|
|
||||||
entry.set("10");
|
|
||||||
assertEquals(10.0, entry.getInt());
|
|
||||||
|
|
||||||
entry.set("10,0");
|
|
||||||
assertThrows(NumberFormatException.class, entry::getInt);
|
|
||||||
|
|
||||||
entry.set(null);
|
|
||||||
assertEquals(0, entry.getInt());
|
|
||||||
assertEquals(11, entry.getIntOr(11));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetDouble() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", null);
|
|
||||||
|
|
||||||
entry.set(1.0);
|
|
||||||
assertEquals(1.0, entry.getDouble());
|
|
||||||
|
|
||||||
entry.set("1.5");
|
|
||||||
assertEquals(1.5, entry.getDouble());
|
|
||||||
|
|
||||||
entry.set("10");
|
|
||||||
assertEquals(10.0, entry.getDouble());
|
|
||||||
|
|
||||||
entry.set("10,0");
|
|
||||||
assertThrows(NumberFormatException.class, entry::getDouble);
|
|
||||||
|
|
||||||
entry.set(null);
|
|
||||||
assertEquals(0.0, entry.getDouble());
|
|
||||||
assertEquals(11.5, entry.getDoubleOr(11.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetBoolean() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", null);
|
|
||||||
|
|
||||||
entry.set(false);
|
|
||||||
assertFalse(entry.getBoolean());
|
|
||||||
|
|
||||||
entry.set("false");
|
|
||||||
assertFalse(entry.getBoolean());
|
|
||||||
|
|
||||||
entry.set("invalid");
|
|
||||||
assertFalse(entry.getBoolean());
|
|
||||||
|
|
||||||
entry.set(1);
|
|
||||||
assertFalse(entry.getBoolean());
|
|
||||||
|
|
||||||
entry.set(true);
|
|
||||||
assertTrue(entry.getBoolean());
|
|
||||||
|
|
||||||
entry.set("true");
|
|
||||||
assertTrue(entry.getBoolean());
|
|
||||||
|
|
||||||
entry.set(null);
|
|
||||||
assertFalse(entry.getBoolean());
|
|
||||||
assertTrue(entry.getBooleanOr(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetStringList() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", null);
|
|
||||||
|
|
||||||
final List<String> fallbackValue = Collections.unmodifiableList(new LinkedList<>());
|
|
||||||
|
|
||||||
entry.set(null);
|
|
||||||
assertNull(entry.getStringList());
|
|
||||||
assertSame(fallbackValue, entry.getStringListOr(fallbackValue));
|
|
||||||
|
|
||||||
entry.set(Collections.singletonList("value"));
|
|
||||||
assertEquals(Collections.singletonList("value"), entry.getStringList());
|
|
||||||
|
|
||||||
entry.set(new String[] {"value2"});
|
|
||||||
assertEquals(Collections.singletonList("value2"), entry.getStringList());
|
|
||||||
|
|
||||||
entry.set("string-value");
|
|
||||||
assertNull(entry.getStringList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetMaterial() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", null);
|
|
||||||
|
|
||||||
entry.set("LOG");
|
|
||||||
assertEquals(CompatibleMaterial.BIRCH_LOG, entry.getMaterial());
|
|
||||||
|
|
||||||
entry.set("OAK_LOG");
|
|
||||||
assertEquals(CompatibleMaterial.OAK_LOG, entry.getMaterial());
|
|
||||||
|
|
||||||
entry.set("10");
|
|
||||||
assertNull(entry.getMaterial());
|
|
||||||
|
|
||||||
entry.set(null);
|
|
||||||
assertNull(entry.getMaterial());
|
|
||||||
assertEquals(CompatibleMaterial.ACACIA_BOAT, entry.getMaterialOr(CompatibleMaterial.ACACIA_BOAT));
|
|
||||||
|
|
||||||
entry.set(CompatibleMaterial.GRASS);
|
|
||||||
assertEquals(CompatibleMaterial.GRASS, entry.getMaterial());
|
|
||||||
|
|
||||||
entry.set(Material.GRASS);
|
|
||||||
assertEquals(CompatibleMaterial.GRASS, entry.getMaterial());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testInvalidWithUpgradeNull() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", "value");
|
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> entry.withUpgradeStep(1, null, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testEqualsAndHashCode() {
|
|
||||||
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
|
|
||||||
ConfigEntry entry = cfg.createEntry("key", "value");
|
|
||||||
|
|
||||||
assertEquals(entry, entry);
|
|
||||||
assertEquals(entry.hashCode(), entry.hashCode());
|
|
||||||
|
|
||||||
|
|
||||||
ConfigEntry other = new YamlConfigEntry(cfg, "key", "value");
|
|
||||||
assertEquals(entry, other);
|
|
||||||
assertEquals(entry.hashCode(), other.hashCode());
|
|
||||||
|
|
||||||
other = new YamlConfigEntry(cfg, "key", "value2");
|
|
||||||
assertNotEquals(entry, other);
|
|
||||||
assertNotEquals(entry.hashCode(), other.hashCode());
|
|
||||||
|
|
||||||
other = new YamlConfigEntry(cfg, "key2", "value");
|
|
||||||
assertNotEquals(entry, other);
|
|
||||||
assertNotEquals(entry.hashCode(), other.hashCode());
|
|
||||||
|
|
||||||
other = new YamlConfigEntry(cfg, "key", "value2");
|
|
||||||
assertNotEquals(entry, other);
|
|
||||||
assertNotEquals(entry.hashCode(), other.hashCode());
|
|
||||||
|
|
||||||
other = new YamlConfigEntry(cfg, "key2", "value2");
|
|
||||||
assertNotEquals(entry, other);
|
|
||||||
assertNotEquals(entry.hashCode(), other.hashCode());
|
|
||||||
|
|
||||||
assertNotEquals(entry, null);
|
|
||||||
assertNotEquals(entry, "key");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,613 +0,0 @@
|
||||||
package com.songoda.core.configuration.yaml;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.yaml.snakeyaml.constructor.DuplicateKeyException;
|
|
||||||
import org.yaml.snakeyaml.error.YAMLException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class YamlConfigurationTest {
|
|
||||||
static final String inputYaml = "foo: bar\n" +
|
|
||||||
"primitives:\n" +
|
|
||||||
" int: " + Integer.MIN_VALUE + "\n" +
|
|
||||||
" long: " + Long.MIN_VALUE + "\n" +
|
|
||||||
" float: " + Float.MIN_VALUE + "\n" +
|
|
||||||
" double: " + Double.MIN_VALUE + "\n" +
|
|
||||||
" char: ä\n" +
|
|
||||||
" string: string\n" +
|
|
||||||
" string-long: " + StringUtils.repeat("abc", 512) + "\n" +
|
|
||||||
" string-multi-line: |\n" +
|
|
||||||
" a\n" +
|
|
||||||
" b\n" +
|
|
||||||
" c\n" +
|
|
||||||
" boolean: true\n" +
|
|
||||||
" list: [2, 1, 3]\n" +
|
|
||||||
" map:\n" +
|
|
||||||
" key: value\n" +
|
|
||||||
" set:\n" +
|
|
||||||
" - 1\n" +
|
|
||||||
" - 2\n" +
|
|
||||||
" - 3\n";
|
|
||||||
static final String expectedOutYaml = "foo: bar\n" +
|
|
||||||
"primitives:\n" +
|
|
||||||
" int: " + Integer.MIN_VALUE + "\n" +
|
|
||||||
" long: " + Long.MIN_VALUE + "\n" +
|
|
||||||
" float: " + Float.MIN_VALUE + "\n" +
|
|
||||||
" double: " + Double.MIN_VALUE + "\n" +
|
|
||||||
" char: ä\n" +
|
|
||||||
" string: string\n" +
|
|
||||||
" string-long: " + StringUtils.repeat("abc", 512) + "\n" +
|
|
||||||
" string-multi-line: |\n" +
|
|
||||||
" a\n" +
|
|
||||||
" b\n" +
|
|
||||||
" c\n" +
|
|
||||||
" boolean: true\n" +
|
|
||||||
" list:\n" +
|
|
||||||
" - 2\n" +
|
|
||||||
" - 1\n" +
|
|
||||||
" - 3\n" +
|
|
||||||
" map:\n" +
|
|
||||||
" key: value\n" +
|
|
||||||
" set:\n" +
|
|
||||||
" - 1\n" +
|
|
||||||
" - 2\n" +
|
|
||||||
" - 3\n";
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testYamlParser() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
cfg.load(new StringReader(inputYaml));
|
|
||||||
|
|
||||||
assertEquals(Integer.MIN_VALUE, cfg.get("primitives.int"));
|
|
||||||
assertEquals(Long.MIN_VALUE, cfg.get("primitives.long"));
|
|
||||||
assertEquals(Float.MIN_VALUE, ((Number) cfg.get("primitives.float")).floatValue());
|
|
||||||
assertEquals(Double.MIN_VALUE, cfg.get("primitives.double"));
|
|
||||||
|
|
||||||
assertEquals("ä", cfg.get("primitives.char"));
|
|
||||||
|
|
||||||
assertEquals("string", cfg.get("primitives.string"));
|
|
||||||
|
|
||||||
assertInstanceOf(Boolean.class, cfg.get("primitives.boolean"));
|
|
||||||
assertTrue((Boolean) cfg.get("primitives.boolean"));
|
|
||||||
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.list");
|
|
||||||
assertNotNull(primitivesList);
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.list"));
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals(2, primitivesList.get(0));
|
|
||||||
assertEquals(1, primitivesList.get(1));
|
|
||||||
assertEquals(3, primitivesList.get(2));
|
|
||||||
|
|
||||||
assertEquals("value", cfg.get("primitives.map.key"));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.set"));
|
|
||||||
assertEquals(3, ((List<?>) cfg.get("primitives.set")).size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testYamlParserWithEmptyFile() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
cfg.load(new StringReader(""));
|
|
||||||
assertTrue(cfg.getKeys("").isEmpty());
|
|
||||||
|
|
||||||
cfg.load(new StringReader("\n"));
|
|
||||||
assertTrue(cfg.getKeys("").isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testYamlParserWithDuplicateKeys() {
|
|
||||||
assertThrowsExactly(DuplicateKeyException.class,
|
|
||||||
() -> new YamlConfiguration().load(new StringReader("test: value1\ntest: value2")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testYamlParserWithInvalidReader() throws IOException {
|
|
||||||
Reader reader = new StringReader("");
|
|
||||||
reader.close();
|
|
||||||
|
|
||||||
assertThrowsExactly(YAMLException.class, () -> new YamlConfiguration().load(reader));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testYamlWriter() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
final StringWriter stringWriter = new StringWriter(inputYaml.length());
|
|
||||||
|
|
||||||
cfg.load(new StringReader(inputYaml));
|
|
||||||
cfg.save(stringWriter);
|
|
||||||
|
|
||||||
assertEquals(expectedOutYaml, stringWriter.toString());
|
|
||||||
assertEquals(expectedOutYaml, cfg.toYamlString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testYamlWriterWithNullValue() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
final StringWriter stringWriter = new StringWriter(1);
|
|
||||||
|
|
||||||
cfg.set("null-value", null);
|
|
||||||
cfg.set("nested.null-value", null);
|
|
||||||
cfg.save(stringWriter);
|
|
||||||
|
|
||||||
assertEquals("", stringWriter.toString());
|
|
||||||
assertEquals("", cfg.toYamlString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testYamlWriterWithNoData() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
final StringWriter stringWriter = new StringWriter(inputYaml.length());
|
|
||||||
|
|
||||||
cfg.save(stringWriter);
|
|
||||||
|
|
||||||
assertEquals("", stringWriter.toString());
|
|
||||||
assertEquals("", cfg.toYamlString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testYamlWriterWithNoDataAndComments() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
final StringWriter stringWriter = new StringWriter(inputYaml.length());
|
|
||||||
|
|
||||||
cfg.setHeaderComment("baz");
|
|
||||||
cfg.setNodeComment("foo", "bar");
|
|
||||||
|
|
||||||
cfg.save(stringWriter);
|
|
||||||
|
|
||||||
assertEquals("# baz\n", stringWriter.toString());
|
|
||||||
assertEquals("# baz\n", cfg.toYamlString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetter() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("foo.bar.innerBar", "bar")); // 'foo.bar' gets overwritten
|
|
||||||
|
|
||||||
Object prevValue = cfg.set("foo.bar", "baz");
|
|
||||||
assertInstanceOf(Map.class, prevValue);
|
|
||||||
assertEquals(1, ((Map<?, ?>) prevValue).size());
|
|
||||||
assertEquals("bar", ((Map<?, ?>) prevValue).get("innerBar"));
|
|
||||||
|
|
||||||
assertNull(cfg.set("number", 27));
|
|
||||||
assertNull(cfg.set("bar.foo.faa1", "value1"));
|
|
||||||
assertNull(cfg.set("bar.foo.faa2", "value2"));
|
|
||||||
|
|
||||||
assertFalse(cfg.has("a.b.c"));
|
|
||||||
assertFalse(cfg.has("a"));
|
|
||||||
|
|
||||||
Map<String, Object> expectedValues = new HashMap<String, Object>() {{
|
|
||||||
put("number", 27);
|
|
||||||
|
|
||||||
put("foo", new HashMap<String, Object>() {{
|
|
||||||
put("bar", "baz");
|
|
||||||
}});
|
|
||||||
|
|
||||||
put("bar", new HashMap<String, Object>() {{
|
|
||||||
put("foo", new HashMap<String, Object>() {{
|
|
||||||
put("faa1", "value1");
|
|
||||||
put("faa2", "value2");
|
|
||||||
}});
|
|
||||||
}});
|
|
||||||
}};
|
|
||||||
|
|
||||||
assertEquals(expectedValues, cfg.values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterAndGetterWithPrimitiveValues() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("foobar", "test"));
|
|
||||||
assertNull(cfg.set("foo.bar", "test2"));
|
|
||||||
assertEquals("test", cfg.set("foobar", "overwritten-test"));
|
|
||||||
|
|
||||||
assertEquals("overwritten-test", cfg.get("foobar"));
|
|
||||||
|
|
||||||
assertEquals("test2", cfg.get("foo.bar"));
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.int", Integer.MIN_VALUE));
|
|
||||||
assertNull(cfg.set("primitives.long", Long.MIN_VALUE));
|
|
||||||
assertNull(cfg.set("primitives.float", Float.MIN_VALUE));
|
|
||||||
assertNull(cfg.set("primitives.double", Double.MIN_VALUE));
|
|
||||||
assertNull(cfg.set("primitives.char", 'ä'));
|
|
||||||
assertNull(cfg.set("primitives.string", "string"));
|
|
||||||
assertNull(cfg.set("primitives.boolean", true));
|
|
||||||
|
|
||||||
assertEquals(Integer.MIN_VALUE, cfg.get("primitives.int"));
|
|
||||||
assertEquals(Long.MIN_VALUE, cfg.get("primitives.long"));
|
|
||||||
|
|
||||||
assertInstanceOf(Double.class, cfg.get("primitives.float"));
|
|
||||||
assertEquals(Float.MIN_VALUE, ((Number) cfg.get("primitives.float")).floatValue());
|
|
||||||
|
|
||||||
assertEquals(Double.MIN_VALUE, cfg.get("primitives.double"));
|
|
||||||
|
|
||||||
assertInstanceOf(String.class, cfg.get("primitives.char"));
|
|
||||||
assertEquals("ä", cfg.get("primitives.char"));
|
|
||||||
|
|
||||||
assertEquals("string", cfg.get("primitives.string"));
|
|
||||||
assertInstanceOf(Boolean.class, cfg.get("primitives.boolean"));
|
|
||||||
assertTrue((Boolean) cfg.get("primitives.boolean"));
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.map.key", "value"));
|
|
||||||
assertEquals("value", cfg.get("primitives.map.key"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithNullValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("foo.bar.null", "not-null-string"));
|
|
||||||
assertEquals("not-null-string", cfg.set("foo.bar.null", null));
|
|
||||||
|
|
||||||
assertNull(cfg.get("foo.bar.null"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetNonExistingNestedKey() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
cfg.load(new StringReader(inputYaml));
|
|
||||||
|
|
||||||
assertNull(cfg.get("primitives.map2.key"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetOrDefault() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
cfg.load(new StringReader(inputYaml));
|
|
||||||
|
|
||||||
assertEquals("bar", cfg.set("foo", "bar"));
|
|
||||||
assertNull(cfg.set("bar.baz", "foz"));
|
|
||||||
|
|
||||||
assertEquals("bar", cfg.getOr("foo", "baz"));
|
|
||||||
assertEquals("foz", cfg.getOr("bar.baz", "baz"));
|
|
||||||
|
|
||||||
assertEquals("default", cfg.getOr("foo.bar", "default"));
|
|
||||||
assertEquals("default", cfg.getOr("bar.baz.foo", "default"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetterWithNullKey() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.get(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetKeys() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
cfg.load(new StringReader(inputYaml));
|
|
||||||
|
|
||||||
assertEquals(2, cfg.getKeys("").size());
|
|
||||||
assertTrue(cfg.getKeys(null).isEmpty());
|
|
||||||
|
|
||||||
assertTrue(cfg.getKeys("primitives.map.key.non-existing-subkey").isEmpty());
|
|
||||||
assertTrue(cfg.getKeys("foo").isEmpty());
|
|
||||||
|
|
||||||
assertArrayEquals(new String[] {"key"}, cfg.getKeys("primitives.map").toArray());
|
|
||||||
assertArrayEquals(new String[] {"int", "long", "float", "double", "char", "string", "string-long", "string-multi-line", "boolean", "list", "map", "set"}, cfg.getKeys("primitives").toArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithListValues() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.list", Arrays.asList(2, 1, 3)));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.list"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.list");
|
|
||||||
assertNotNull(primitivesList);
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals(2, primitivesList.get(0));
|
|
||||||
assertEquals(1, primitivesList.get(1));
|
|
||||||
assertEquals(3, primitivesList.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithEnumValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.enum", TestEnum.ENUM_VALUE));
|
|
||||||
|
|
||||||
assertInstanceOf(String.class, cfg.get("primitives.enum"));
|
|
||||||
assertEquals(TestEnum.ENUM_VALUE, TestEnum.valueOf((String) cfg.get("primitives.enum")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithBooleanArrayValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.array", new boolean[] {Boolean.FALSE, Boolean.TRUE}));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.array"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.array");
|
|
||||||
assert primitivesList != null;
|
|
||||||
assertEquals(2, primitivesList.size());
|
|
||||||
assertEquals(Boolean.FALSE, primitivesList.get(0));
|
|
||||||
assertEquals(Boolean.TRUE, primitivesList.get(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithByteArrayValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.array", new byte[] {2, Byte.MIN_VALUE, Byte.MAX_VALUE}));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.array"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.array");
|
|
||||||
assert primitivesList != null;
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals(2, primitivesList.get(0));
|
|
||||||
assertEquals((int) Byte.MIN_VALUE, primitivesList.get(1));
|
|
||||||
assertEquals((int) Byte.MAX_VALUE, primitivesList.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithCharArrayValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.array", new char[] {'x', Character.MIN_VALUE, Character.MAX_VALUE}));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.array"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.array");
|
|
||||||
assert primitivesList != null;
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals("x", primitivesList.get(0));
|
|
||||||
assertEquals(String.valueOf(Character.MIN_VALUE), primitivesList.get(1));
|
|
||||||
assertEquals(String.valueOf(Character.MAX_VALUE), primitivesList.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithShortArrayValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.array", new short[] {2, Short.MIN_VALUE, Short.MAX_VALUE}));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.array"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.array");
|
|
||||||
assert primitivesList != null;
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals(2, primitivesList.get(0));
|
|
||||||
assertEquals((int) Short.MIN_VALUE, primitivesList.get(1));
|
|
||||||
assertEquals((int) Short.MAX_VALUE, primitivesList.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithIntArrayValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.array", new int[] {2, Integer.MIN_VALUE, Integer.MAX_VALUE}));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.array"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.array");
|
|
||||||
assert primitivesList != null;
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals(2, primitivesList.get(0));
|
|
||||||
assertEquals(Integer.MIN_VALUE, primitivesList.get(1));
|
|
||||||
assertEquals(Integer.MAX_VALUE, primitivesList.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithLongArrayValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.array", new long[] {2, Long.MIN_VALUE, Long.MAX_VALUE}));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.array"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.array");
|
|
||||||
assert primitivesList != null;
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals((long) 2, primitivesList.get(0));
|
|
||||||
assertEquals(Long.MIN_VALUE, primitivesList.get(1));
|
|
||||||
assertEquals(Long.MAX_VALUE, primitivesList.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithFloatArrayValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.array", new float[] {2, Float.MIN_VALUE, Float.MAX_VALUE}));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.array"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.array");
|
|
||||||
assert primitivesList != null;
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals((double) 2, primitivesList.get(0));
|
|
||||||
assertEquals((double) Float.MIN_VALUE, primitivesList.get(1));
|
|
||||||
assertEquals((double) Float.MAX_VALUE, primitivesList.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithDoubleArrayValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.array", new double[] {2, Double.MIN_VALUE, Double.MAX_VALUE}));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.array"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.array");
|
|
||||||
assert primitivesList != null;
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals((double) 2, primitivesList.get(0));
|
|
||||||
assertEquals(Double.MIN_VALUE, primitivesList.get(1));
|
|
||||||
assertEquals(Double.MAX_VALUE, primitivesList.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSetterWithStringArrayValue() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertNull(cfg.set("primitives.array", new String[] {"zyx", "b", "a"}));
|
|
||||||
|
|
||||||
assertInstanceOf(List.class, cfg.get("primitives.array"));
|
|
||||||
List<?> primitivesList = (List<?>) cfg.get("primitives.array");
|
|
||||||
assert primitivesList != null;
|
|
||||||
assertEquals(3, primitivesList.size());
|
|
||||||
assertEquals("zyx", primitivesList.get(0));
|
|
||||||
assertEquals("b", primitivesList.get(1));
|
|
||||||
assertEquals("a", primitivesList.get(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testHas() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
assertFalse(cfg.has(null));
|
|
||||||
|
|
||||||
assertNull(cfg.set("foo", "bar"));
|
|
||||||
|
|
||||||
assertTrue(cfg.has("foo"));
|
|
||||||
assertFalse(cfg.has("bar"));
|
|
||||||
|
|
||||||
assertNull(cfg.set("foo.bar", "baz"));
|
|
||||||
assertTrue(cfg.has("foo.bar"));
|
|
||||||
|
|
||||||
assertFalse(cfg.has("foo.baz"));
|
|
||||||
assertNull(YamlConfiguration.getInnerMap(cfg.values, new String[] {"foo", "baz"}, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testReset() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
cfg.load(new StringReader(inputYaml));
|
|
||||||
|
|
||||||
cfg.setNodeComment("foo", "bar");
|
|
||||||
cfg.setHeaderComment("baz");
|
|
||||||
|
|
||||||
assertFalse(cfg.values.isEmpty());
|
|
||||||
cfg.reset();
|
|
||||||
assertTrue(cfg.values.isEmpty());
|
|
||||||
|
|
||||||
assertNotNull(cfg.getHeaderComment());
|
|
||||||
assertNotNull(cfg.getNodeComment("foo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testUnset() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
cfg.load(new StringReader(inputYaml));
|
|
||||||
|
|
||||||
Object unsetResult;
|
|
||||||
|
|
||||||
assertTrue(cfg.has("foo"));
|
|
||||||
unsetResult = cfg.unset("foo");
|
|
||||||
assertEquals("bar", unsetResult);
|
|
||||||
assertFalse(cfg.has("foo"));
|
|
||||||
|
|
||||||
assertTrue(cfg.has("primitives"));
|
|
||||||
assertTrue(cfg.has("primitives.int"));
|
|
||||||
assertTrue(cfg.has("primitives.double"));
|
|
||||||
unsetResult = cfg.unset("primitives.int");
|
|
||||||
assertEquals(Integer.MIN_VALUE, unsetResult);
|
|
||||||
assertFalse(cfg.has("primitives.int"));
|
|
||||||
assertTrue(cfg.has("primitives.double"));
|
|
||||||
|
|
||||||
unsetResult = cfg.unset("primitives");
|
|
||||||
assertInstanceOf(Map.class, unsetResult);
|
|
||||||
assertFalse(cfg.has("primitives"));
|
|
||||||
assertFalse(cfg.has("primitives.double"));
|
|
||||||
assertFalse(cfg.has("primitives.string"));
|
|
||||||
|
|
||||||
unsetResult = cfg.unset("unknown.nested.key");
|
|
||||||
assertNull(unsetResult);
|
|
||||||
unsetResult = cfg.unset("unknown-key");
|
|
||||||
assertNull(unsetResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testToString() throws IOException {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
String firstToString = cfg.toString();
|
|
||||||
|
|
||||||
assertTrue(firstToString.contains(YamlConfiguration.class.getSimpleName()));
|
|
||||||
assertTrue(firstToString.contains(cfg.values.toString()));
|
|
||||||
|
|
||||||
cfg.load(new StringReader(inputYaml));
|
|
||||||
|
|
||||||
String secondToString = cfg.toString();
|
|
||||||
|
|
||||||
assertNotEquals(firstToString, secondToString);
|
|
||||||
assertTrue(secondToString.contains(YamlConfiguration.class.getSimpleName()));
|
|
||||||
assertTrue(secondToString.contains(cfg.values.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testLoadWithInvalidYaml() {
|
|
||||||
final YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
|
|
||||||
IllegalStateException exception = assertThrowsExactly(IllegalStateException.class,
|
|
||||||
() -> cfg.load(new StringReader("Hello world")));
|
|
||||||
|
|
||||||
assertEquals("The YAML file does not have the expected tree structure: java.lang.String", exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testHeaderComments() throws IOException {
|
|
||||||
String expectedHeaderComment = "This is a header comment";
|
|
||||||
|
|
||||||
YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
cfg.setHeaderComment(expectedHeaderComment);
|
|
||||||
cfg.set("foo", "bar");
|
|
||||||
|
|
||||||
assertNotNull(cfg.getHeaderComment());
|
|
||||||
assertEquals(expectedHeaderComment, cfg.getHeaderComment().get());
|
|
||||||
|
|
||||||
assertEquals("# " + expectedHeaderComment + "\n\nfoo: bar\n", cfg.toYamlString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testNodeComments() throws IOException {
|
|
||||||
String expectedYaml = "# Foo-Comment\n" +
|
|
||||||
"foo: bar\n" +
|
|
||||||
"# Level1-Comment\n" +
|
|
||||||
"level1:\n" +
|
|
||||||
" level2:\n" +
|
|
||||||
" # Level3-Comment\n" +
|
|
||||||
" level3: value\n";
|
|
||||||
|
|
||||||
YamlConfiguration cfg = new YamlConfiguration();
|
|
||||||
cfg.set("foo", "bar");
|
|
||||||
cfg.set("level1.level2.level3", "value");
|
|
||||||
|
|
||||||
cfg.setNodeComment("foo", "Foo-Comment");
|
|
||||||
cfg.setNodeComment("level1", "Level1-Comment");
|
|
||||||
cfg.setNodeComment("level1.level2.level3", "Level3-Comment");
|
|
||||||
|
|
||||||
Supplier<String> currentNodeComment = cfg.getNodeComment("foo");
|
|
||||||
assertNotNull(currentNodeComment);
|
|
||||||
assertEquals("Foo-Comment", currentNodeComment.get());
|
|
||||||
|
|
||||||
currentNodeComment = cfg.getNodeComment("level1");
|
|
||||||
assertNotNull(currentNodeComment);
|
|
||||||
assertEquals("Level1-Comment", currentNodeComment.get());
|
|
||||||
|
|
||||||
currentNodeComment = cfg.getNodeComment("level1.level2");
|
|
||||||
assertNull(currentNodeComment);
|
|
||||||
|
|
||||||
currentNodeComment = cfg.getNodeComment("level1.level2.level3");
|
|
||||||
assertNotNull(currentNodeComment);
|
|
||||||
assertEquals("Level3-Comment", currentNodeComment.get());
|
|
||||||
|
|
||||||
assertEquals(expectedYaml, cfg.toYamlString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum TestEnum {
|
|
||||||
ENUM_VALUE;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "#toString(): " + super.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,160 +0,0 @@
|
||||||
package com.songoda.core.locale;
|
|
||||||
|
|
||||||
import com.songoda.core.http.MockHttpClient;
|
|
||||||
import com.songoda.core.http.MockHttpResponse;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
class LocaleFileManagerTest {
|
|
||||||
private final byte[] validIndexFile = ("# This is a comment\n\nen_US.lang\nen.yml\nde.txt\n").getBytes(StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
private Path testDirectoryPath;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() throws IOException {
|
|
||||||
this.testDirectoryPath = Files.createTempDirectory("SongodaCore-LocaleFileManagerTest");
|
|
||||||
this.testDirectoryPath.toFile().deleteOnExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
void tearDown() throws IOException {
|
|
||||||
try (Stream<Path> paths = Files.list(this.testDirectoryPath)) {
|
|
||||||
for (Path path : paths.toArray(Path[]::new)) {
|
|
||||||
Files.deleteIfExists(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Files.deleteIfExists(this.testDirectoryPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void downloadMissingTranslations_EmptyTargetDir() throws IOException {
|
|
||||||
Plugin plugin = Mockito.mock(Plugin.class);
|
|
||||||
Mockito.when(plugin.getDataFolder()).thenReturn(this.testDirectoryPath.toFile());
|
|
||||||
|
|
||||||
MockHttpClient httpClient = new MockHttpClient(new MockHttpResponse(200, this.validIndexFile));
|
|
||||||
LocaleFileManager localeFileManager = new LocaleFileManager(httpClient, "test");
|
|
||||||
|
|
||||||
List<String> downloadedFiles = localeFileManager.downloadMissingTranslations(plugin.getDataFolder());
|
|
||||||
|
|
||||||
Assertions.assertSame(3, downloadedFiles.size());
|
|
||||||
Assertions.assertEquals("en.yml", downloadedFiles.get(1));
|
|
||||||
Assertions.assertEquals("en_US.lang", downloadedFiles.get(0));
|
|
||||||
Assertions.assertEquals("de.txt", downloadedFiles.get(2));
|
|
||||||
|
|
||||||
String[] localeFiles = plugin.getDataFolder().list();
|
|
||||||
Assertions.assertNotNull(localeFiles);
|
|
||||||
Arrays.sort(localeFiles);
|
|
||||||
Assertions.assertArrayEquals(new String[] {"de.txt", "en.yml", "en_US.lang"}, localeFiles);
|
|
||||||
|
|
||||||
Assertions.assertSame(4, httpClient.callsOnGet.size());
|
|
||||||
Assertions.assertTrue(httpClient.callsOnGet.get(0).contains("/test/_index.txt"));
|
|
||||||
Assertions.assertTrue(httpClient.callsOnGet.get(1).contains("/test/en_US.lang"));
|
|
||||||
Assertions.assertTrue(httpClient.callsOnGet.get(2).contains("/test/en.yml"));
|
|
||||||
Assertions.assertTrue(httpClient.callsOnGet.get(3).contains("/test/de.txt"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void downloadMissingTranslations() throws IOException {
|
|
||||||
Plugin plugin = Mockito.mock(Plugin.class);
|
|
||||||
Mockito.when(plugin.getDataFolder()).thenReturn(this.testDirectoryPath.toFile());
|
|
||||||
|
|
||||||
Files.createDirectories(plugin.getDataFolder().toPath());
|
|
||||||
Files.createFile(new File(plugin.getDataFolder(), "en_US.lang").toPath());
|
|
||||||
Files.createFile(new File(plugin.getDataFolder(), "fr.lang").toPath());
|
|
||||||
|
|
||||||
MockHttpClient httpClient = new MockHttpClient(new MockHttpResponse(200, this.validIndexFile));
|
|
||||||
LocaleFileManager localeFileManager = new LocaleFileManager(httpClient, "test");
|
|
||||||
|
|
||||||
List<String> downloadedFiles = localeFileManager.downloadMissingTranslations(plugin.getDataFolder());
|
|
||||||
|
|
||||||
Assertions.assertSame(2, downloadedFiles.size());
|
|
||||||
Assertions.assertEquals("en.yml", downloadedFiles.get(0));
|
|
||||||
Assertions.assertEquals("de.txt", downloadedFiles.get(1));
|
|
||||||
|
|
||||||
String[] localeFiles = plugin.getDataFolder().list();
|
|
||||||
|
|
||||||
Assertions.assertNotNull(localeFiles);
|
|
||||||
Arrays.sort(localeFiles);
|
|
||||||
Assertions.assertArrayEquals(new String[] {"de.txt", "en.yml", "en_US.lang", "fr.lang"}, localeFiles);
|
|
||||||
|
|
||||||
Assertions.assertSame(3, httpClient.callsOnGet.size());
|
|
||||||
Assertions.assertTrue(httpClient.callsOnGet.get(0).contains("/test/_index.txt"));
|
|
||||||
Assertions.assertTrue(httpClient.callsOnGet.get(1).contains("/test/en.yml"));
|
|
||||||
Assertions.assertTrue(httpClient.callsOnGet.get(2).contains("/test/de.txt"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void fetchAvailableLanguageFiles() throws IOException {
|
|
||||||
MockHttpClient httpClient = new MockHttpClient(new MockHttpResponse(200, this.validIndexFile));
|
|
||||||
LocaleFileManager localeFileManager = new LocaleFileManager(httpClient, "test");
|
|
||||||
|
|
||||||
List<String> availableLanguages = localeFileManager.fetchAvailableLanguageFiles();
|
|
||||||
|
|
||||||
Assertions.assertSame(1, httpClient.callsOnGet.size());
|
|
||||||
Assertions.assertTrue(httpClient.callsOnGet.get(0).contains("/test/"));
|
|
||||||
|
|
||||||
Assertions.assertNotNull(availableLanguages);
|
|
||||||
Assertions.assertSame(3, availableLanguages.size());
|
|
||||||
Assertions.assertTrue(availableLanguages.contains("en_US.lang"));
|
|
||||||
Assertions.assertTrue(availableLanguages.contains("en.yml"));
|
|
||||||
Assertions.assertTrue(availableLanguages.contains("de.txt"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void fetchAvailableLanguageFiles_SpecialCharsInProjectName() throws IOException {
|
|
||||||
MockHttpClient httpClient = new MockHttpClient(new MockHttpResponse(200, this.validIndexFile));
|
|
||||||
LocaleFileManager localeFileManager = new LocaleFileManager(httpClient, "test project (special)");
|
|
||||||
|
|
||||||
List<String> availableLanguages = localeFileManager.fetchAvailableLanguageFiles();
|
|
||||||
|
|
||||||
Assertions.assertSame(1, httpClient.callsOnGet.size());
|
|
||||||
Assertions.assertTrue(httpClient.callsOnGet.get(0).contains("/test+project+%28special%29/"));
|
|
||||||
|
|
||||||
Assertions.assertNotNull(availableLanguages);
|
|
||||||
Assertions.assertSame(3, availableLanguages.size());
|
|
||||||
Assertions.assertTrue(availableLanguages.contains("en_US.lang"));
|
|
||||||
Assertions.assertTrue(availableLanguages.contains("en.yml"));
|
|
||||||
Assertions.assertTrue(availableLanguages.contains("de.txt"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void fetchAvailableLanguageFiles_EmptyIndex() throws IOException {
|
|
||||||
MockHttpClient httpClient = new MockHttpClient(new MockHttpResponse(200, new byte[0]));
|
|
||||||
LocaleFileManager localeFileManager = new LocaleFileManager(httpClient, "empty-project");
|
|
||||||
|
|
||||||
List<String> availableLanguages = localeFileManager.fetchAvailableLanguageFiles();
|
|
||||||
|
|
||||||
Assertions.assertNotNull(availableLanguages);
|
|
||||||
Assertions.assertTrue(availableLanguages.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void fetchAvailableLanguageFiles_UnknownProject() throws IOException {
|
|
||||||
MockHttpClient httpClient = new MockHttpClient(new MockHttpResponse(404, new byte[0]));
|
|
||||||
LocaleFileManager localeFileManager = new LocaleFileManager(httpClient, "unknown");
|
|
||||||
|
|
||||||
Assertions.assertNull(localeFileManager.fetchAvailableLanguageFiles());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void fetchAvailableLanguageFiles_HttpStatus500() {
|
|
||||||
MockHttpClient httpClient = new MockHttpClient(new MockHttpResponse(500, new byte[0]));
|
|
||||||
LocaleFileManager localeFileManager = new LocaleFileManager(httpClient, "test");
|
|
||||||
|
|
||||||
Assertions.assertThrows(IOException.class, localeFileManager::fetchAvailableLanguageFiles);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue