ConfigLoader.java
package team.aura_dev.auraban.platform.common.config;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.reflect.TypeToken;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
import java.util.regex.Pattern;
import lombok.Getter;
import lombok.NonNull;
import ninja.leaping.configurate.ConfigurationOptions;
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
import ninja.leaping.configurate.loader.ConfigurationLoader;
import ninja.leaping.configurate.loader.HeaderMode;
import ninja.leaping.configurate.objectmapping.ObjectMappingException;
import team.aura_dev.auraban.platform.common.AuraBanBase;
/** This class is required because it references Configurate classes, which are loaded later. */
public class ConfigLoader {
private final AuraBanBase plugin;
private ConfigurationLoader<CommentedConfigurationNode> loader = null;
@Getter @NonNull private Config config;
public ConfigLoader(AuraBanBase plugin) {
this.plugin = plugin;
this.loader = getConfigLoader();
}
private ConfigurationLoader<CommentedConfigurationNode> getConfigLoader() {
return HoconConfigurationLoader.builder()
.setDefaultOptions(
ConfigurationOptions.defaults()
.setHeader(
"######################################################################################################################### #\n"
+ "+-----------------------------------------------------------------------------------------------------------------------+ #\n"
+ getBanner()
+ "| | #\n"
+ "| Source Code: https://github.com/AuraDevelopmentTeam/AuraBan/ | #\n"
+ "| Bug Reports: https://github.com/AuraDevelopmentTeam/AuraBan/issues | #\n"
+ "| Wiki: https://aura-dev.team/documentation/AuraBan | #\n"
+ "| | #\n"
+ "| New options ARE added to this file automatically. Removing a setting will have it be regenerated with its default | #\n"
+ "| value. This config format (HOCON: https://github.com/lightbend/config/blob/master/HOCON.md) is very lenient, so | #\n"
+ "| so don't worry about messing up the formatting or anything. After every reload the config will be nice and | #\n"
+ "| formatted. You should be able to find existing highlighter settings for your favorite text editor, so you can have | #\n"
+ "| nice syntax highlighing like for other config formats like YAML, JSON or TOML. | #\n"
+ "+-----------------------------------------------------------------------------------------------------------------------+ #\n"
+ "######################################################################################################################### #"))
.setHeaderMode(HeaderMode.PRESET)
// These two are commented out because those are the defaults anyways. Additionally
// SpongeForge relocates HOCON classes, so we can't reference them directly anyways.
//
// .setRenderOptions(ConfigRenderOptions.defaults().setOriginComments(false).setJson(false))
// .setParseOptions(ConfigParseOptions.defaults())
.setPath(plugin.getConfigFile())
.build();
}
/**
* This method takes the plugin banner, removes the color codes, pads it left and right with
* spaces and lastly adds the banner box ASCII art borders to it, so it fits in the block up
* above.<br>
* This is done so that we can have the dynamic banner in the config.
*
* <p>While this sounds a fair bit simpler than it actually is this method is quite long and
* complex.<br>
* Feel free to try to improve it if it bothers you.
*
* @return A version of the plugin banner suitable for the config.
*/
@VisibleForTesting
String getBanner() {
// Constants
final int lineLength = 115;
final char[] spaces = new char[lineLength];
Arrays.fill(spaces, ' ');
final Pattern colorCode = Pattern.compile("ยง[0-9a-fk-or]", Pattern.CASE_INSENSITIVE);
// Variables
List<String> bannerLines = new ArrayList<>(plugin.getAsciiBanner());
int maxLength = 0;
String result;
StringBuilder builder;
// First pass:
// Remove color codes
for (int i = 0; i < bannerLines.size(); ++i) {
result = colorCode.matcher(bannerLines.get(i)).replaceAll("");
bannerLines.set(i, result);
if (maxLength < result.length()) {
maxLength = result.length();
}
}
// The banner starts with two spaces, let's use this to compensate
maxLength += 2;
// Store these values, so we don't have to calculate them again
final int remainingLength = lineLength - maxLength;
final int paddingBefore = remainingLength / 2;
final int paddingAfter = remainingLength - paddingBefore;
// Second pass:
// Add space paddings
for (int i = 0; i < bannerLines.size(); ++i) {
result = bannerLines.get(i);
builder =
new StringBuilder(lineLength)
.append(spaces, 0, paddingBefore)
.append(result)
.append(spaces, 0, maxLength - result.length())
.append(spaces, 0, paddingAfter);
bannerLines.set(i, builder.toString());
}
// Join the string
final StringJoiner joiner = new StringJoiner(" | #\n| ", "| ", " | #\n");
for (final String line : bannerLines) {
joiner.add(line);
}
return joiner.toString();
}
public void loadConfig() throws IOException, ObjectMappingException {
final TypeToken<Config> configToken = TypeToken.of(Config.class);
AuraBanBase.logger.debug("Loading config...");
CommentedConfigurationNode node = loader.load();
try {
config = node.<Config>getValue(configToken, Config::new);
} catch (ObjectMappingException e) {
final String message = e.getMessage();
if (!message.startsWith("Invalid enum constant provided for storageEngine:")) throw e;
final String defaultStorageEngine = (new Config.Storage()).getStorageEngine().name();
AuraBanBase.logger.error(message);
AuraBanBase.logger.warn(
"Possible values are: " + Config.Storage.StorageEngineType.allowedValues);
AuraBanBase.logger.warn(
"To fix your config we changed the storage engine to " + defaultStorageEngine);
node.getNode("storage", "storageEngine").setValue(defaultStorageEngine);
config = node.<Config>getValue(configToken, Config::new);
}
// Make sure the character encoding is set
config.getStorage().getMysql().getPoolSettings().getProperties();
AuraBanBase.logger.debug("Saving/Formatting config...");
node.setValue(configToken, config);
loader.save(node);
}
}