/*
* This file is part of Kenshins Hide and Seek
*
* Copyright (c) 2021 Tyler Murphy.
*
* Kenshins Hide and Seek free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* he Free Software Foundation version 3.
*
* Kenshins Hide and Seek is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
package net.tylermurphy.hideAndSeek.configuration;
import net.tylermurphy.hideAndSeek.Main;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
public class ConfigManager {
private final File file;
private YamlConfiguration config,defaultConfig;
private String defaultFilename;
public static ConfigManager create(String filename) {
return new ConfigManager(filename, filename);
}
public static ConfigManager create(String filename, String defaultFilename) {
return new ConfigManager(filename, defaultFilename);
}
private ConfigManager(String filename, String defaultFilename) {
File dataFolder = Main.getInstance().getDataFolder();
File oldDataFolder = new File(Main.getInstance().getDataFolder().getParent() + File.separator + "HideAndSeek");
this.defaultFilename = defaultFilename;
this.file = new File(dataFolder, filename);
if(oldDataFolder.exists()){
if(!dataFolder.exists()){
if(!oldDataFolder.renameTo(dataFolder)){
throw new RuntimeException("Could not rename folder: " + oldDataFolder.getPath());
}
} else {
throw new RuntimeException("Plugin folders for HideAndSeek & KenshinsHideAndSeek both exists. There can only be one!");
}
}
if (!dataFolder.exists()) {
if (!dataFolder.mkdirs()) {
throw new RuntimeException("Failed to make directory: " + file.getPath());
}
}
if (!file.exists()) {
try{
InputStream input = Main.getInstance().getResource(defaultFilename);
if (input == null) {
throw new RuntimeException("Could not create input stream for "+defaultFilename);
}
java.nio.file.Files.copy(input, file.toPath());
input.close();
} catch(IOException e) {
e.printStackTrace();
}
}
FileInputStream fileInputStream;
try {
fileInputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
throw new RuntimeException("Could not create input stream for "+file.getPath());
}
InputStreamReader reader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
this.config = new YamlConfiguration();
try {
this.config.load(reader);
} catch(InvalidConfigurationException e) {
Main.getInstance().getLogger().severe(e.getMessage());
throw new RuntimeException("Invalid configuration in config file: "+file.getPath());
} catch(IOException e) {
throw new RuntimeException("Could not access file: "+file.getPath());
}
InputStream input = this.getClass().getClassLoader().getResourceAsStream(defaultFilename);
if (input == null) {
throw new RuntimeException("Could not create input stream for "+defaultFilename);
}
InputStreamReader default_reader = new InputStreamReader(input, StandardCharsets.UTF_8);
this.defaultConfig = new YamlConfiguration();
try {
this.defaultConfig.load(default_reader);
} catch(InvalidConfigurationException e) {
Main.getInstance().getLogger().severe(e.getMessage());
throw new RuntimeException("Invalid configuration in internal config file: "+defaultFilename);
} catch(IOException e) {
throw new RuntimeException("Could not access internal file: "+defaultFilename);
}
try{
fileInputStream.close();
default_reader.close();
} catch (IOException e) {
throw new RuntimeException("Unable to finalize loading of config files.");
}
}
public boolean contains(String path) {
return config.contains(path);
}
public double getDouble(String path) {
if (!config.contains(path)) {
return defaultConfig.getDouble(path);
} else {
return config.getDouble(path);
}
}
public int getInt(String path) {
if (!config.contains(path)) {
return defaultConfig.getInt(path);
} else {
return config.getInt(path);
}
}
public int getDefaultInt(String path) {
return defaultConfig.getInt(path);
}
public float getFloat(String path) {
if (!config.contains(path)) {
return (float) defaultConfig.getDouble(path);
} else {
return (float) config.getDouble(path);
}
}
public String getString(String path) {
String value = config.getString(path);
if (value == null) {
return defaultConfig.getString(path);
} else {
return value;
}
}
public String getString(String path, String oldPath) {
String value = config.getString(path);
if (value == null) {
String oldValue = config.getString(oldPath);
if (oldValue == null) {
return defaultConfig.getString(path);
} else {
return oldValue;
}
} else {
return value;
}
}
public List getStringList(String path) {
List value = config.getStringList(path);
if (value == null) {
return defaultConfig.getStringList(path);
} else {
return value;
}
}
public void reset(String path) {
config.set(path, defaultConfig.get(path));
}
public void resetAll() {
config = new YamlConfiguration();
}
public void resetFile(String newDefaultFilename) {
this.defaultFilename = newDefaultFilename;
InputStream input = Main.getInstance().getResource(defaultFilename);
if (input == null) {
throw new RuntimeException("Could not create input stream for "+defaultFilename);
}
InputStreamReader reader = new InputStreamReader(input);
this.config = YamlConfiguration.loadConfiguration(reader);
this.defaultConfig = YamlConfiguration.loadConfiguration(reader);
}
public boolean getBoolean(String path) {
if (!config.contains(path)) {
return defaultConfig.getBoolean(path);
} else {
return config.getBoolean(path);
}
}
public ConfigurationSection getConfigurationSection(String path) {
ConfigurationSection section = config.getConfigurationSection(path);
if (section == null) {
return defaultConfig.getConfigurationSection(path);
} else {
return section;
}
}
public ConfigurationSection getDefaultConfigurationSection(String path) {
return defaultConfig.getConfigurationSection(path);
}
public void set(String path, Object value) {
config.set(path, value);
}
public void overwriteConfig() {
try {
this.config.save(file);
} catch (IOException e) {
e.printStackTrace();
}
}
public void saveConfig() {
try {
// open config file
InputStream is = Main.getInstance().getResource(defaultFilename);
// if failed error
if (is == null) {
throw new RuntimeException("Could not create input stream for "+defaultFilename);
}
// manually read in each character to preserve string data
StringBuilder textBuilder = new StringBuilder(new String("".getBytes(), StandardCharsets.UTF_8));
Reader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
int c;
while((c = reader.read()) != -1)
textBuilder.append((char) c);
// store yaml file into a string
String yaml = new String(textBuilder.toString().getBytes(), StandardCharsets.UTF_8);
// get config values
Map data = config.getValues(true);
// write each stored config value into the yaml string
for(Map.Entry entry: data.entrySet()) {
// if type isn't supported, skip
if(!isSupported(entry.getValue())) continue;
// get index of key in yaml string
int index = getIndex(yaml, entry.getKey());
// if index not found, skip
if (index < 10) continue;
// get start and end of the value
int start = yaml.indexOf(' ', index) + 1;
int end = yaml.indexOf('\n', index);
// if end not found, set it to the end of the file
if (end == -1) end = yaml.length();
// create new replace sting
StringBuilder replace = new StringBuilder(new String("".getBytes(), StandardCharsets.UTF_8));
// get value
Object value = entry.getValue();
// if the value is a list,
if (value instanceof List) {
end = yaml.indexOf(']', start) + 1;
List> list = (List>) entry.getValue();
if (list.isEmpty()) {
// if list is empty, put an empty list
replace.append("[]");
} else {
// if list has values, populate values into the string
// get gap before key
int gap = whitespaceBefore(yaml, index);
String space = new String(new char[gap]).replace('\0', ' ');
replace.append("[\n");
for (int i = 0; i < list.size(); i++) {
replace.append(space).append(" ").append(convert(list.get(i)));
if(i != list.size() -1) replace.append(",\n");
}
replace.append('\n').append(space).append("]");
}
// otherwise just put the value directly
} else {
replace.append(convert(value));
}
// replace the new value in the yaml string
StringBuilder builder = new StringBuilder(yaml);
builder.replace(start, end, replace.toString());
yaml = builder.toString();
}
// write yaml string to file
Writer fileWriter = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8));
fileWriter.write(yaml);
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private int getIndex(String yaml, String key) {
String[] parts = key.split("\\.");
int index = 0;
for(String part : parts) {
if (index == 0) {
index = yaml.indexOf(part + ":", index);
} else {
index = yaml.indexOf(" " + part + ":", index) + 1;
}
if (index == -1) break;
}
return index;
}
public boolean isSupported(Object o) {
return o instanceof Integer ||
o instanceof Double ||
o instanceof String ||
o instanceof Boolean ||
o instanceof List;
}
public int whitespaceBefore(String yaml, int index) {
int count = 0;
for(int i = index - 1; yaml.charAt(i) == ' '; i--) count++;
return count;
}
private String convert(Object o) {
if(o instanceof String) {
return "\"" + o + "\"";
} else if (o instanceof Boolean) {
return (boolean)o ? "true" : "false";
}
return o.toString();
}
}