package com.austinv11.collectiveframework.minecraft.config;
import com.austinv11.collectiveframework.minecraft.CollectiveFramework;
import com.austinv11.collectiveframework.utils.ArrayUtils;
import com.austinv11.collectiveframework.utils.ReflectionUtils;
import cpw.mods.fml.relauncher.Side;
import net.minecraftforge.common.MinecraftForge;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
/**
* A registry for configs
*/
public class ConfigRegistry {
private static List<ConfigProxy> toBeInitialized = new ArrayList<ConfigProxy>();
public static List<ConfigProxy> configs = new ArrayList<ConfigProxy>();
private static List<IConfigProxy> proxies = new ArrayList<IConfigProxy>();
static {
registerConfigProxy(new DefaultProxy());
}
/**
* Registers a config with the registry
* @param config The config to register
* @throws ConfigException
*/
public static void registerConfig(Object config) throws ConfigException {
if (!config.getClass().isAnnotationPresent(Config.class))
throw new ConfigException("Config "+config.toString()+" does not contain a Config annotation!");
Config configAnnotation = config.getClass().getAnnotation(Config.class);
try {
ConfigProxy configProxy = new ConfigProxy(configAnnotation, config);
toBeInitialized.add(configProxy);
} catch (Exception e) {
e.printStackTrace();
throw new ConfigException(e.getMessage());
}
}
/**
* Registers a config proxy
* @param proxy The config proxy
*/
public static void registerConfigProxy(IConfigProxy proxy) {
proxies.add(proxy);
}
/**
* Only meant for internal use
*/
public static void init() {
for (ConfigProxy config : toBeInitialized)
initialize(config);
toBeInitialized.clear();
}
private static void initialize(ConfigProxy configProxy) {
IConfigurationHandler handler = configProxy.handler;
ConfigLoadEvent.Init event = new ConfigLoadEvent.Init();
event.config = handler.convertToString(configProxy.config);
event.configName = configProxy.fileName;
event.isRevert = false;
MinecraftForge.EVENT_BUS.post(event);
handler.loadFile(configProxy.fileName, configProxy.config, configProxy.fields);
configs.add(configProxy);
}
/**
* Gets the key for the passed object
* @param o The object
* @return The key
*/
public static String getKey(Object o) {
for (IConfigProxy proxy : proxies)
if (proxy.canSerializeObject(o))
return proxy.getKey(o);
return "@NULL@";
}
/**
* Serializes the passed object into a string
* @param o The object to serialize
* @return The serialized string
* @throws ConfigException
*/
public static String serialize(Object o) throws ConfigException {
for (IConfigProxy proxy : proxies)
if (proxy.canSerializeObject(o))
return proxy.serialize(o);
return "@NULL@";
}
/**
* Deserializes the passed string with the passed key
* @param key The key representing the object type
* @param string The serialized string
* @return The deserialized object
* @throws ConfigException
*/
public static Object deserialize(String key, String string) throws ConfigException {
for (IConfigProxy proxy : proxies)
if (proxy.isKeyUsable(key))
return proxy.deserialize(key, string);
return null;
}
/**
* The default {@link com.austinv11.collectiveframework.minecraft.config.IConfigurationHandler} for configs
*/
public static class DefaultConfigurationHandler implements IConfigurationHandler {
private Map<String, Map<String, Field>> current = new HashMap<String, Map<String, Field>>();
private File cachedFile;
@Override
public void setValue(String configValue, String category, Object value, Object config) {
setValue(configValue, category, value, config, true);
}
@Override
public void setValue(String configValue, String category, Object value, Object config, boolean saveToFile) {
Map<String, Field> fields = current.containsKey(category) ? current.get(category) : new HashMap<String,Field>();
Field field = fields.containsKey(configValue) ? fields.get(configValue) : ReflectionUtils.getDeclaredOrNormalField(configValue, config.getClass());
try {
field.set(config, value);
} catch (IllegalAccessException e) {
e.printStackTrace(); //This should never be reached
}
if (saveToFile)
try {
writeFile(cachedFile, config);
} catch (Exception e) {
e.printStackTrace();
}
if (!current.containsKey(category) || !fields.containsKey(configValue)) {
fields.put(configValue, field);
current.put(category, fields);
}
}
@Override
public Object getValue(String configValue, String category, Object config) {
if (hasValue(configValue, category)) {
if (current.containsKey(category) && current.get(category).containsKey(configValue))
return current.get(category).get(configValue);
}
return null;
}
@Override
public void loadFile(String fileName, Object config, Map<String, Map<String, Field>> hint) {
current = hint;
cachedFile = new File("./config/"+fileName);
if (cachedFile.exists())
try {
readFile(cachedFile, config);
} catch (Exception e) {
e.printStackTrace();
}
else {
try {
cachedFile.createNewFile();
writeFile(cachedFile, config);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void loadFromString(String string, Object config, Map<String, Map<String, Field>> hint) {
try {
readFromReader(new BufferedReader(new StringReader(string)), config);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public File getConfigFile(String fileName, Object config) {
return cachedFile;
}
@Override
public boolean hasValue(String configValue, String category) {
return current.containsKey(category) && current.get(category).containsKey(configValue);
}
@Override
public String convertToString(Object config) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
writeToStream(new PrintStream(stream), config);
return stream.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private void writeToStream(PrintStream writer, Object config) throws IllegalAccessException, ConfigException {
for (String category : current.keySet()) {
int i = 0;
for (String field : current.get(category).keySet()) {
Field f = current.get(category).get(field);
f.setAccessible(true);
if (f.isAnnotationPresent(Description.class) && f.getAnnotation(Description.class).clientSideOnly() &&
CollectiveFramework.proxy.getSide() == Side.SERVER)
continue;
if (i == 0)
writer.println(category+" {");
String comment = f.isAnnotationPresent(Description.class) ? f.getAnnotation(Description.class).comment() : "None! Tell the mod author to include a comment!";
writer.println("\t"+comment);
writer.println("\t"+getKey(f.get(config))+":"+field+"="+serialize(f.get(config)));
writer.println();
i++;
if (i == current.get(category).size())
writer.println("}");
}
}
writer.flush();
writer.close();
}
private void writeFile(File file, Object config) throws IOException, IllegalAccessException, ConfigException {
PrintStream writer = new PrintStream(file);
writeToStream(writer, config);
}
private void readFile(File file, Object config) throws IOException, IllegalAccessException, ClassNotFoundException, InstantiationException {
BufferedReader reader = new BufferedReader(new FileReader(file));
readFromReader(reader, config);
try {
writeFile(file, config);
} catch (ConfigException e) {
e.printStackTrace();
}
}
private void readFromReader(BufferedReader reader, Object config) throws IOException, IllegalAccessException {
String line;
boolean reachedBracket = false;
int lineCount = 0;
while ((line = reader.readLine()) != null) {
if (!reachedBracket && line.contains("{")) {
reachedBracket = true;
continue;
}
if (reachedBracket && line.equals("}")) {
reachedBracket = false;
continue;
}
if (reachedBracket) {
if (lineCount < 1) {
lineCount++;
} else if (lineCount == 1) {
String field = line.substring(line.indexOf(":")+1, line.indexOf("="));
String key = line.substring(0, line.indexOf(":")).replace("\t", "").trim();
line = line.substring(line.indexOf("=")+1);
Field f = ReflectionUtils.getDeclaredOrNormalField(field, config.getClass());
if (f != null) {
try {
f.setAccessible(true);
f.set(config, deserialize(key, line));
} catch (ConfigException e) {
e.printStackTrace();
}
}
lineCount++;
} else {
lineCount = 0;
}
}
}
}
}
public static class ConfigProxy implements Cloneable {
public Object config;
public IConfigurationHandler handler;
public String fileName;
public boolean doesSync;
public TreeMap<String, Map<String, Field>> fields = new TreeMap<String, Map<String, Field>>();
public ConfigProxy(Config annotation, Object config) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
this.config = config;
this.handler = (IConfigurationHandler) Class.forName(annotation.handler()).newInstance();
this.fileName = annotation.fileName().equals("@NULL@") ? config.getClass().getSimpleName()+".cfg" : annotation.fileName();
this.doesSync = annotation.doesSync();
Field[] declared = config.getClass().getDeclaredFields();
for (Field f : declared) {
f.setAccessible(true);
if (ArrayUtils.indexOf(annotation.exclude(), f.getName()) == -1) {
if (f.isAnnotationPresent(Description.class))
addToCategory(f.getAnnotation(Description.class).category(), f);
else
addToCategory("General", f);
}
}
Field[] field = config.getClass().getFields();
for (Field f : field) {
f.setAccessible(true);
if (ArrayUtils.indexOf(annotation.exclude(), f.getName()) == -1)
if (f.isAnnotationPresent(Description.class))
addToCategory(f.getAnnotation(Description.class).category(), f);
else
addToCategory("General", f);
}
}
private void addToCategory(String category, Field f) {
Map<String, Field> vals;
if (fields.containsKey(category))
vals = fields.get(category);
else
vals = new TreeMap<String, Field>();
vals.put(f.getName(), f);
fields.put(category, vals);
}
}
public static void onConfigReload(ConfigLoadEvent.Pre event) {
if (!event.isCanceled()) {
if (!event.isRevert) {
CollectiveFramework.LOGGER.info("Reloading config '"+event.configName+"'");
ConfigProxy proxy = findConfigProxyForConfigFile(configs, event.configName);
if (proxy == null) {
CollectiveFramework.LOGGER.error("There was an error reloading the config!");
return;
}
proxy.handler.loadFromString(event.config, proxy.config, proxy.fields);
} else {
CollectiveFramework.LOGGER.info("Reverting config '"+event.configName+"'");
ConfigProxy proxy = findConfigProxyForConfigFile(configs, event.configName);
if (proxy == null) {
CollectiveFramework.LOGGER.error("There was an error reverting the config!");
return;
}
proxy.handler.loadFile(event.config, proxy.config, proxy.fields);
}
}
}
private static ConfigProxy findConfigProxyForConfigFile(List<ConfigProxy> proxies, String filename) {
for (ConfigProxy proxy : proxies)
if (proxy.fileName.equals(filename))
return proxy;
return null;
}
}