Skip to content

Instantly share code, notes, and snippets.

@Articdive
Last active July 12, 2018 18:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Articdive/7edd4d8c420fc5c6aa8ff88d92256be1 to your computer and use it in GitHub Desktop.
Save Articdive/7edd4d8c420fc5c6aa8ff88d92256be1 to your computer and use it in GitHub Desktop.
CommentedConfiguration all important stuffs.
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.apache.commons.lang.Validate;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.file.YamlConstructor;
import org.bukkit.configuration.file.YamlRepresenter;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
/**
* @author dumptruckman, LlmDL & Articdive
*/
public class CommentedConfiguration extends YamlConfiguration {
private final DumperOptions yamlOptions = new DumperOptions();
private final Representer yamlRepresenter = new YamlRepresenter();
private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions);
private HashMap<String, String> comments;
private File file;
public CommentedConfiguration(File file) {
super();
//this.load(file);
comments = new HashMap<>();
this.file = file;
}
public boolean load() {
boolean loaded = true;
try {
this.load(file);
} catch (InvalidConfigurationException | IOException e) {
loaded = false;
}
return loaded;
}
public void save() {
boolean saved = true;
// Save the config just like normal
try {
/*
* Doing some saving of our own. We have found that the implementation of YAMLComfiguration used by Bukkit will attempt to
* cap strings at 80 characters long, forming new lines in some of our longer strings (channel_formats.)
*/
this.save(file);
} catch (Exception e) {
saved = false;
}
// if there's comments to add and it saved fine, we need to add comments
if (!comments.isEmpty() && saved) {
// String array of each line in the config file
String[] yamlContents = FileMgmt.convertFileToString(file).split("[" + System.getProperty("line.separator") + "]");
// This will hold the newly formatted line
StringBuilder newContents = new StringBuilder();
// This holds the current path the lines are at in the config
String currentPath = "";
// This flags if the line is a node or unknown text.
boolean node;
// The depth of the path. (number of words separated by periods - 1)
int depth = 0;
// Loop through the config lines
for (String line : yamlContents) {
// If the line is a node (and not something like a list value)
if (line.contains(": ") || (line.length() > 1 && line.charAt(line.length() - 1) == ':')) {
// This is a node so flag it as one
node = true;
// Grab the index of the end of the node name
int index;
index = line.indexOf(": ");
if (index < 0) {
index = line.length() - 1;
}
// If currentPath is empty, store the node name as the currentPath. (this is only on the first iteration, i think)
if (currentPath.isEmpty()) {
currentPath = line.substring(0, index);
} else {
// Calculate the whitespace preceding the node name
int whiteSpace = 0;
for (int n = 0; n < line.length(); n++) {
if (line.charAt(n) == ' ') {
whiteSpace++;
} else {
break;
}
}
// Find out if the current depth (whitespace * 2) is greater/lesser/equal to the previous depth
if (whiteSpace / 2 > depth) {
// Path is deeper. Add a . and the node name
currentPath += "." + line.substring(whiteSpace, index);
depth++;
} else if (whiteSpace / 2 < depth) {
// Path is shallower, calculate current depth from whitespace (whitespace / 2) and subtract that many levels from the currentPath
int newDepth = whiteSpace / 2;
for (int i = 0; i < depth - newDepth; i++) {
currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), "");
}
// Grab the index of the final period
int lastIndex = currentPath.lastIndexOf(".");
if (lastIndex < 0) {
// if there isn't a final period, set the current path to nothing because we're at root
currentPath = "";
} else {
// If there is a final period, replace everything after it with nothing
currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), "");
currentPath += ".";
}
// Add the new node name to the path
currentPath += line.substring(whiteSpace, index);
// Reset the depth
depth = newDepth;
} else {
// Path is same depth, replace the last path node name to the current node name
int lastIndex = currentPath.lastIndexOf(".");
if (lastIndex < 0) {
// if there isn't a final period, set the current path to nothing because we're at root
currentPath = "";
} else {
// If there is a final period, replace everything after it with nothing
currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), "");
currentPath += ".";
}
//currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), "");
currentPath += line.substring(whiteSpace, index);
}
}
} else
node = false;
if (node) {
String comment;
// If there's a comment for the current path, retrieve it and flag that path as already commented
comment = comments.get(currentPath);
if (comment != null) {
// Add the comment to the beginning of the current line
line = comment + System.getProperty("line.separator") + line + System.getProperty("line.separator");
} else {
// Add a new line as it is a node, but has no comment
line += System.getProperty("line.separator");
}
}
// Add the (modified) line to the total config String
newContents.append(line).append((!node) ? System.getProperty("line.separator") : "");
}
/*
* Due to a bukkit bug we need to strip any extra new lines from the
* beginning of this file, else they will multiply.
*/
while (newContents.toString().startsWith(System.getProperty("line.separator")))
newContents = new StringBuilder(newContents.toString().replaceFirst(System.getProperty("line.separator"), ""));
// Write the string to the config file
FileMgmt.stringToFile(newContents.toString(), file);
}
}
/**
* Adds a comment just before the specified path. The comment can be
* multiple lines. An empty string will indicate a blank line.
*
* @param path Configuration path to add comment.
* @param commentLines Comments to add. One String per line.
*/
public void addComment(String path, String... commentLines) {
StringBuilder commentstring = new StringBuilder();
StringBuilder leadingSpaces = new StringBuilder();
for (int n = 0; n < path.length(); n++) {
if (path.charAt(n) == '.') {
leadingSpaces.append(" ");
}
}
for (String line : commentLines) {
if (!line.isEmpty()) {
line = leadingSpaces + line;
} else {
line = "";
}
if (commentstring.length() > 0) {
commentstring.append(System.getProperty("line.separator"));
}
commentstring.append(line);
}
comments.put(path, commentstring.toString());
}
public void save(File file) throws IOException {
Validate.notNull(file, "File cannot be null");
Files.createParentDirs(file);
String data = this.saveToString();
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)) {
writer.write(data);
}
}
@Override
public String saveToString() {
yamlOptions.setIndent(options().indent());
yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
yamlOptions.setWidth(10000);
yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
String dump = yaml.dump(getValues(false));
if (dump.equals(BLANK_CONFIG)) {
dump = "";
}
return dump;
}
}
public enum ConfigNodes {
// Version:
VERSION_HEADER("version", ""),
VERSION(
"version.version",
"",
"# This is the current config version. Please do not edit."),
LAST_RUN_VERSION(
"version.last_run_version",
"",
"# This is the last version the config was run with, it's used to show changelogs! Please do not edit."),
// Database:
DATABASE_HEADER("database", "",
"############################################################",
"# +------------------------------------------------------+ #",
"# | Database Configuration | #",
"# +------------------------------------------------------+ #",
"############################################################"),
DATABASE_TABLE_PREFIX("database.table_prefix", "SWC_", "# Table prefix for SQL tables"),
// Database LOAD:
DATABASE_LOAD_HEADER("database.load", ""),
DATABASE_LOAD_TYPE("database.load.type", "h2", "# Load database type (options: h2, mysql)"),
DATABASE_LOAD_HOSTNAME("database.load.hostname", "localhost", "# Load database hostname"),
DATABASE_LOAD_PORT("database.load.port", "3306", "# Load database port"),
DATABASE_LOAD_SCHEMA_NAME("database.load.dbname", "townyeco", "# Load database schema / database"),
DATABASE_LOAD_USERNAME("database.load.username", "", "# Load database username"),
DATABASE_LOAD_PASSWORD("database.load.password", "", "# Load database password"),
// Database SAVE:
DATABASE_SAVE_HEADER("database.save", ""),
DATABASE_SAVE_TYPE("database.save.type", "h2", "# Save database type (options: h2, mysql)"),
DATABASE_SAVE_HOSTNAME("database.save.hostname", "localhost", "# Save database hostname"),
DATABASE_SAVE_PORT("database.save.port", "3306", "# Save database port"),
DATABASE_SAVE_SCHEMA_NAME("database.save.dbname", "townyeco", "# Save database schema / database"),
DATABASE_SAVE_USERNAME("database.save.username", "", "# Save database username"),
DATABASE_SAVE_PASSWORD("database.save.password", "", "# Save database password");
private final String Root;
private final String Default;
private String[] comments;
ConfigNodes(String root, String def, String... comments) {
this.Root = root;
this.Default = def;
this.comments = comments;
}
/**
* Retrieves the root for a config option
*
* @return The root for a config option
*/
public String getRoot() {
return Root;
}
/**
* Retrieves the default value for a config path
*
* @return The default value for a config path
*/
public String getDefault() {
return Default;
}
/**
* Retrieves the comment for a config path
*
* @return The comments for a config path
*/
public String[] getComments() {
if (comments != null) {
return comments;
}
String[] comments = new String[1];
comments[0] = "";
return comments;
}
}
import java.io.File;
import java.util.logging.Level;
public class ConfigurationHandler {
private static MainClass main;
private static CommentedConfiguration config, newConfig;
public static void initialize(MainClass main) {
ConfigurationHandler.main = main;
}
public static boolean loadSettings() {
FileMgmt.checkFolders(new String[]{
main.getRootFolder(),
main.getRootFolder() + FileMgmt.fileSeparator() + "settings"});
return ConfigurationHandler.loadConfig();
}
private static boolean loadConfig() {
String filepath = main.getRootFolder() + FileMgmt.fileSeparator() + "settings" + FileMgmt.fileSeparator() + "config.yml";
File file = FileMgmt.CheckYMLExists(new File(filepath));
if (file != null) {
// read the config.yml into memory
config = new CommentedConfiguration(file);
if (!config.load()) {
main.getLogger().log(Level.SEVERE, "Failed to load config.yml");
return false;
}
setDefaults(file);
config.save();
}
return true;
}
/**
* Builds a new config reading old config data.
*/
private static void setDefaults(File file) {
newConfig = new CommentedConfiguration(file);
newConfig.load();
for (ConfigNodes root : ConfigNodes.values()) {
if (root.getComments().length > 0) {
addComment(root.getRoot(), root.getComments());
}
if (root.getRoot().equals(ConfigNodes.VERSION.getRoot())) {
setNewProperty(root.getRoot(), main.getVersion());
} else
setNewProperty(root.getRoot(), (config.get(root.getRoot().toLowerCase()) != null) ? config.get(root.getRoot().toLowerCase()) : root.getDefault());
}
config = newConfig;
newConfig = null;
}
private static void addComment(String root, String... comments) {
newConfig.addComment(root.toLowerCase(), comments);
}
private static void setProperty(String root, Object value) {
config.set(root.toLowerCase(), value.toString());
}
private static void setNewProperty(String root, Object value) {
if (value == null) {
// System.out.print("value is null for " + root.toLowerCase());
value = "";
}
newConfig.set(root.toLowerCase(), value.toString());
}
/**
* Get's a value for a ConfigNode
*
* @param node - ConfigNode
* @return - Value for node
*/
private static String getString(ConfigNodes node) {
return config.getString(node.getRoot().toLowerCase(), node.getDefault());
}
/**
* Get's a value for a ConfigNode
*
* @param node - ConfigNode
* @return - Value for node (specifically boolean)
*/
private static boolean getBoolean(ConfigNodes node) {
return Boolean.parseBoolean(config.getString(node.getRoot().toLowerCase(), node.getDefault()));
}
/**
* Get's a value for a ConfigNode
*
* @param node - ConfigNode
* @return - Value for node (specifically double)
*/
private static double getDouble(ConfigNodes node) {
try {
return Double.parseDouble(config.getString(node.getRoot().toLowerCase(), node.getDefault()).trim());
} catch (NumberFormatException e) {
main.getLogger().log(Level.SEVERE, "Could not get/read double for value: " + node.getRoot().toLowerCase());
return 0.0;
}
}
/**
* Get's a value for a ConfigNode
*
* @param node - ConfigNode
* @return - Value for node (specifically int)
*/
private static int getInt(ConfigNodes node) {
try {
return Integer.parseInt(config.getString(node.getRoot().toLowerCase(), node.getDefault()).trim());
} catch (NumberFormatException e) {
main.getLogger().log(Level.SEVERE, "Could not get/read int for value: " + node.getRoot().toLowerCase());
return 0;
}
}
public static String getDBTablePrefix() {
return getString(ConfigNodes.DATABASE_TABLE_PREFIX);
}
public static String getLoadDBType() {
return getString(ConfigNodes.DATABASE_LOAD_TYPE).toLowerCase();
}
public static String getLoadDBHostname() {
return getString(ConfigNodes.DATABASE_LOAD_HOSTNAME);
}
public static String getLoadDBPort() {
return getString(ConfigNodes.DATABASE_LOAD_PORT);
}
public static String getLoadDBSchemaName() {
return getString(ConfigNodes.DATABASE_LOAD_SCHEMA_NAME);
}
public static String getLoadDBUsername() {
return getString(ConfigNodes.DATABASE_LOAD_USERNAME);
}
public static String getLoadDBPassword() {
return getString(ConfigNodes.DATABASE_LOAD_PASSWORD);
}
public static String getSaveDBType() {
return getString(ConfigNodes.DATABASE_SAVE_TYPE).toLowerCase();
}
public static String getSaveDBHostname() {
return getString(ConfigNodes.DATABASE_SAVE_HOSTNAME);
}
public static String getSaveDBPort() {
return getString(ConfigNodes.DATABASE_SAVE_PORT);
}
public static String getSaveDBSchemaName() {
return getString(ConfigNodes.DATABASE_SAVE_SCHEMA_NAME);
}
public static String getSaveDBUsername() {
return getString(ConfigNodes.DATABASE_SAVE_USERNAME);
}
public static String getSaveDBPassword() {
return getString(ConfigNodes.DATABASE_SAVE_PASSWORD);
}
}
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.logging.Level;
public class FileMgmt {
private static MainClass main;
public static void initialize(MainClass main) {
FileMgmt.main = main;
}
public static void checkFolders(String[] folders) {
for (String folder : folders) {
File f = new File(folder);
if (!(f.exists() && f.isDirectory())) {
f.getParentFile().mkdirs();
f.mkdir();
}
}
}
public static String fileSeparator() {
return System.getProperty("file.separator");
}
public static File CheckYMLExists(File file) {
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
/**
* Pass a file and it will return it's contents as a string.
*
* @param file File to read.
* @return Contents of file. String will be empty in case of any errors.
*/
public static String convertFileToString(File file) {
if (file != null && file.exists() && file.canRead() && !file.isDirectory()) {
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try (InputStream is = new FileInputStream(file)) {
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
reader.close();
} catch (IOException e) {
main.getLogger().log(Level.SEVERE, "Exception: " + e.getMessage());
}
return writer.toString();
} else {
return "";
}
}
/**
* Writes the contents of a string to a file.
*
* @param source String to write.
* @param file File to write to.
* @return True on success.
* @throws IOException
*/
public static void stringToFile(String source, File file) {
try {
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
out.write(source);
out.close();
} catch (IOException e) {
main.getLogger().log(Level.SEVERE, "Exception: " + e.getMessage());
}
}
}
import de.articdive.spigotormtemplate.utilities.FileMgmt;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.logging.Level;
public final class MainClass extends JavaPlugin {
private String version;
@Override
public void onEnable() {
version = this.getDescription().getVersion();
FileMgmt.initialize(this);
ConfigurationHandler.initialize(this);
// We need a configuration
if (!ConfigurationHandler.loadSettings()) {
getLogger().log(Level.SEVERE, "Configuration could not be loaded, disabling...");
Bukkit.getPluginManager().disablePlugin(this);
return;
}
}
@Override
public void onDisable() {
// Plugin shutdown logic
}
public String getRootFolder() {
return this.getDataFolder().getPath();
}
public String getVersion() {
return version;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment