ShopChest/core/src/main/java/de/epiceric/shopchest/config/ConfigManager.java

210 lines
7.3 KiB
Java

package de.epiceric.shopchest.config;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Properties;
import java.util.StringJoiner;
import de.epiceric.shopchest.ShopChestImpl;
import de.epiceric.shopchest.api.config.Config;
import de.epiceric.shopchest.api.config.MaterialDoubleMap;
import de.epiceric.shopchest.api.config.MaterialList;
import de.epiceric.shopchest.api.config.Property;
import de.epiceric.shopchest.api.config.StringList;
import de.epiceric.shopchest.util.Logger;
/**
* Class for accessing ShopChest configuration values
*/
public class ConfigManager {
private static ConfigManager instance;
/**
* Gets the instance of the config manager
*
* @param plugin an instance of the {@link ShopChestImpl plugin implementation}
* @return the config manager
* @see ShopChestImpl#getConfigManager()
*/
public static ConfigManager get(ShopChestImpl plugin) {
if (instance == null) {
instance = new ConfigManager(plugin);
}
return instance;
}
private ShopChestImpl plugin;
private File configFile;
private Properties properties;
private ConfigManager(ShopChestImpl plugin) {
this.plugin = plugin;
this.configFile = new File(plugin.getDataFolder(), "config.properties");
this.properties = new Properties();
}
private String buildHeader() {
return new StringJoiner("\n")
.add("# ===============================================")
.add("# ====== Configuration File of 'ShopChest' ======")
.add("# ===============================================")
.add("")
.add("# You can find item names in the 'item_names.txt' file.")
.add("")
.add("# The documentation for this config file can be found here:")
.add("# https://github.com/EpicEricEE/ShopChest/wiki/Configuration")
.toString();
}
@SuppressWarnings("unchecked")
private <T> T deserialize(String str, Class<T> type) {
if (str == null) {
return null;
}
if (type == String.class) {
return (T) str;
}
if (type.isEnum()) {
try {
Method methodName = type.getMethod("name");
for (T enumVal : type.getEnumConstants()) {
String name = (String) methodName.invoke(enumVal);
if (name.equalsIgnoreCase(str)) {
return enumVal;
}
}
} catch (ReflectiveOperationException e) {
Logger.severe("Failed to parse \"{0}\" as enum {1}", str, type.getName());
Logger.severe(e);
}
}
try {
Method methodValueOf = type.getMethod("valueOf", String.class);
return (T) methodValueOf.invoke(null, str);
} catch (ReflectiveOperationException e) {
Logger.severe("Failed to parse \"{0}\" as {1}", str, type.getName());
Logger.severe(e);
}
return null;
}
private String doubleToString(double d) {
if (d == (int) d) {
return String.valueOf((int) d);
}
return String.valueOf(d);
}
private <T> String serialize(T value, Class<T> type) {
if (type.isEnum()) {
return ((Enum<?>) value).name().toLowerCase(Locale.US);
} else if (type == String.class) {
return (String) value;
} else if (type == MaterialDoubleMap.class) {
return ((MaterialDoubleMap) value).toString();
} else if (type == MaterialList.class) {
return ((MaterialList) value).toString();
} else if (type == StringList.class) {
return ((StringList) value).toString();
} else if (type == Double.class) {
return doubleToString(((Double) value).doubleValue());
} else if (type == Float.class) {
return doubleToString(((Float) value).doubleValue());
}
return String.valueOf(value);
}
private <T> void loadProperty(Property<T> property) {
property.set(deserialize(properties.getProperty(property.getKey()), property.getType()));
}
private <T> void saveProperty(Property<T> property) {
properties.setProperty(property.getKey(), serialize(property.get(), property.getType()));
}
/**
* Loads the config file and sets all properties' values
*
* @throws IOException if the properties file cannot be loaded
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public void load() throws IOException {
if (!configFile.exists()) {
configFile.getParentFile().mkdirs();
plugin.saveResource("config.properties", false);
}
FileInputStream inputStream = new FileInputStream(configFile);
properties.load(inputStream);
boolean hasNewProperties = false;
for (Field field : Config.class.getFields()) {
if (field.getType() == Property.class) {
try {
Property property = (Property) field.get(null);
if (!properties.containsKey(property.getKey())) {
hasNewProperties = true;
String strVal = serialize(property.get(), property.getType());
properties.setProperty(property.getKey(), strVal);
Logger.warning("New config property \"{0}\" has been added with a default value of \"{1}\"",
property.getKey(), strVal);
}
loadProperty(property);
} catch (ReflectiveOperationException e) {
Logger.severe("Failed to load property \"{0}\"", field.getName());
Logger.severe(e);
}
}
}
if (hasNewProperties) {
// Save new properties to file
save();
}
}
/**
* Saves all properties' values to the config file
*
* @throws IOException if the properties file cannot be written to
*/
public void save() throws IOException {
FileWriter writer = new FileWriter(configFile);
writer.write(buildHeader());
String sectionBefore = "";
for (Field field : Config.class.getFields()) {
if (field.getType() == Property.class) {
try {
Property<?> property = (Property<?>) field.get(null);
saveProperty(property);
if (!sectionBefore.equals(property.getSection())) {
// Empty line between sections
writer.write("\n");
sectionBefore = property.getSection();
}
writer.write("\n" + property.getKey() + "=" + properties.getProperty(property.getKey()));
} catch (ReflectiveOperationException | IOException e) {
Logger.severe("Failed to save property \"{0}\"", field.getName());
Logger.severe(e);
}
}
}
writer.close();
}
}