blob: 4a5cfc434b98d960b8f2179825d490c278a17ae1 [file] [log] [blame] [raw]
package us.myles.ViaVersion.util;
import us.myles.ViaVersion.api.configuration.ConfigurationProvider;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.lang.reflect.Field;
public abstract class Config implements ConfigurationProvider {
private static final ThreadLocal<Gson> GSON = new ThreadLocal<Gson>() {
@Override
protected Gson initialValue() {
String date_time_format_pattern = get_date_format_pattern() + " HH:mm:ss";
return (new GsonBuilder()).disableHtmlEscaping().setDateFormat(date_time_format_pattern).setPrettyPrinting().create();
}
};
private static Field jsonwriter_indent_field;
private static Field jsonwriter_separator_field;
static {
try {
jsonwriter_indent_field = JsonWriter.class.getDeclaredField("indent");
jsonwriter_indent_field.setAccessible(true);
} catch(NoSuchFieldException e) {
e.printStackTrace();
}
try {
jsonwriter_separator_field = JsonWriter.class.getDeclaredField("separator");
jsonwriter_separator_field.setAccessible(true);
} catch(NoSuchFieldException e) {
e.printStackTrace();
}
}
private static String get_date_format_pattern() {
DateFormat date_format = DateFormat.getDateInstance(DateFormat.DEFAULT);
return date_format instanceof SimpleDateFormat ?
((SimpleDateFormat)date_format).toPattern() : "d MMM yyyy";
}
private CommentStore commentStore = new CommentStore('.', 1);
private final File configFile;
private ConcurrentSkipListMap<String, Object> config;
/**
* Create a new Config instance, this will *not* load the config by default.
* To load config see {@link #reloadConfig()}
*
* @param configFile The location of where the config is loaded/saved.
*/
public Config(File configFile) {
this.configFile = configFile;
}
public abstract URL getDefaultConfigURL();
private static Map<String, Object> read_from_json(Gson gson, InputStream stream) {
JsonReader reader = new JsonReader(new InputStreamReader(stream));
reader.setLenient(true);
return (Map<String, Object>)gson.fromJson(reader, ConcurrentSkipListMap.class);
}
public synchronized Map<String, Object> loadConfig(File location) {
List<String> unsupported = getUnsupportedOptions();
URL jarConfigFile = getDefaultConfigURL();
try {
commentStore.storeComments(jarConfigFile.openStream());
for (String option : unsupported) {
List<String> comments = commentStore.header(option);
if (comments != null) {
comments.clear();
}
}
} catch (IOException e) {
e.printStackTrace();
}
Map<String, Object> config = null;
if (location.exists()) {
try (FileInputStream input = new FileInputStream(location)) {
config = read_from_json(GSON.get(), input);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
if (config == null) {
config = new HashMap<>();
}
Map<String, Object> defaults = config;
try (InputStream stream = jarConfigFile.openStream()) {
defaults = read_from_json(GSON.get(), stream);
for (String option : unsupported) {
defaults.remove(option);
}
// Merge with defaultLoader
for (Object key : config.keySet()) {
// Set option in new conf if exists
if (defaults.containsKey(key) && !unsupported.contains(key.toString())) {
defaults.put((String) key, config.get(key));
}
}
} catch (IOException e) {
e.printStackTrace();
}
// Call Handler
handleConfig(defaults);
// Save
saveConfig(location, defaults);
return defaults;
}
protected abstract void handleConfig(Map<String, Object> config);
private static String to_json(Gson gson, Map<String, Object> obj) {
StringWriter buffer = new StringWriter();
JsonWriter writer = new JsonWriter(buffer);
try {
if(jsonwriter_indent_field != null) {
jsonwriter_indent_field.set(writer, " ");
} else {
writer.setIndent(" ");
}
if(jsonwriter_separator_field != null) {
jsonwriter_separator_field.set(writer, ":");
}
} catch(IllegalAccessException e) {
throw new RuntimeException(e);
}
writer.setSerializeNulls(false);
gson.toJson(obj, Map.class, writer);
return buffer.toString();
}
public synchronized void saveConfig(File location, Map<String, Object> config) {
try {
commentStore.writeComments(to_json(GSON.get(), config), location);
} catch (IOException e) {
e.printStackTrace();
}
}
public abstract List<String> getUnsupportedOptions();
@Override
public void set(String path, Object value) {
config.put(path, value);
}
@Override
public void saveConfig() {
this.configFile.getParentFile().mkdirs();
saveConfig(this.configFile, this.config);
}
@Override
public void reloadConfig() {
this.configFile.getParentFile().mkdirs();
this.config = new ConcurrentSkipListMap<>(loadConfig(this.configFile));
}
@Override
public Map<String, Object> getValues() {
return this.config;
}
public <T> T get(String key, Class<T> clazz, T def) {
Object o = this.config.get(key);
if (o != null) {
return (T) o;
} else {
return def;
}
}
public boolean getBoolean(String key, boolean def) {
Object o = this.config.get(key);
if (o != null) {
return (boolean) o;
} else {
return def;
}
}
public String getString(String key, String def) {
final Object o = this.config.get(key);
if (o != null) {
return (String) o;
} else {
return def;
}
}
public int getInt(String key, int def) {
Object o = this.config.get(key);
if (o != null) {
if (o instanceof Number) {
return ((Number) o).intValue();
} else {
return def;
}
} else {
return def;
}
}
public double getDouble(String key, double def) {
Object o = this.config.get(key);
if (o != null) {
if (o instanceof Number) {
return ((Number) o).doubleValue();
} else {
return def;
}
} else {
return def;
}
}
public List<Integer> getIntegerList(String key) {
Object o = this.config.get(key);
if (o != null) {
return (List<Integer>) o;
} else {
return new ArrayList<>();
}
}
}