package org.nwapw.abacus.config; import com.moandjiezana.toml.Toml; import com.moandjiezana.toml.TomlWriter; import org.nwapw.abacus.number.NaiveNumber; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; /** * A configuration object, which essentially * manages saving, loading, and getting values * from the configuration. While Configuration is * the data model, this is the interface with it. */ public class ConfigurationObject { /** * The default implementation to use for instantiating numbers. */ private static final Class<? extends NaiveNumber> DEFAULT_IMPLEMENTATION = NaiveNumber.class; /** * The writer used to store the configuration. */ private static final TomlWriter TOML_WRITER = new TomlWriter(); /** * The configuration instance being modeled. */ private Configuration configuration; /** * A map of number names to their implementations, which * will be provided by plugins. */ private Map<String, Class<? extends NaiveNumber>> numberImplementations; /** * Sets up the ConfigurationObject. * different constructors do different things, * but they all lead here. * @param configuration the configuration to set up with. */ private void setup(Configuration configuration){ this.configuration = configuration; numberImplementations = new HashMap<>(); } /** * Creates a default configuration. * @return the newly created default configuration. */ private Configuration getDefaultConfig(){ configuration = new Configuration(); configuration.decimalPrecision = -1; configuration.numberType = "naive"; return configuration; } /** * Register a number implementation. * @param name the name of the number implementation to register the class as. * @param newClass the class that will be used to instantiate the new number. * It is required that this class provides a Number(String) constructor. */ public void registerImplementation(String name, Class<? extends NaiveNumber> newClass){ numberImplementations.put(name, newClass); } /** * Creates a new number with the configured type, passing * it the given string. * @param string the string from which the number should be parsed. * @return the resulting number, or null if an error occurred. */ public NaiveNumber numberFromString(String string) { Class<? extends NaiveNumber> toLoad = numberImplementations.getOrDefault(configuration.numberType, DEFAULT_IMPLEMENTATION); try { return toLoad.getConstructor(String.class).newInstance(string); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); } return null; } /** * Returns the configured, user-requested precision. * @return the precision. */ public int getPrecision(){ return configuration.decimalPrecision; } /** * Saves the ConfigurationObject to the given file. * @param toFile the file to save ot. * @return true if the save succeed, false if otherwise. */ public boolean save(File toFile){ if(toFile.getParentFile() != null) toFile.getParentFile().mkdirs(); try { TOML_WRITER.write(configuration, toFile); return true; } catch (IOException e) { e.printStackTrace(); } return false; } /** * Creates a new configuration object with the given config. * @param config the config to use. */ public ConfigurationObject(Configuration config){ setup(config); } /** * Create a configuration object by attempting to * load a config from the given path, using the * default configuration otherwise. * @param path the path to attempt to load. */ public ConfigurationObject(File path){ Configuration config; if(!path.exists()) { config = getDefaultConfig(); } else { Toml parse = new Toml(); parse.read(path); config = parse.to(Configuration.class); } setup(config); } /** * Creates a new configuration object with the * default configuration. */ public ConfigurationObject(){ setup(getDefaultConfig()); } }