package util.settings; import gui.forms.GUIMain; import util.Utils; import java.awt.*; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * This class was made for the standardization of Settings found in the Settings class. * <p> * We can generify most of the settings by setting them to their types, * then converting them from the Properties Strings back into their * original class types, using the Converter class. * <p> * Settings are now just one line, unless there's a need to add a ChangeListener. * <p> * Adding a ChangeListener to the setting allows for callbacks * to be used elsewhere when the setting is changed. * <p> * A primary example can be when the settings are loaded, * the callbacks are used to update the main GUI to reflect the updated settings. */ public class Setting<E> { private E actualValue, defaultValue; private String nameInFile; private Class<E> type; private ChangeListener<E> listener; /** * Constructor for the setting. * * @param nameInFile The name as stored in the Properties file. * @param defaultValue The default value of the setting. * @param type The class that the value of the setting derives from. */ public Setting(String nameInFile, E defaultValue, Class<E> type) { this.nameInFile = nameInFile; this.defaultValue = defaultValue; this.type = type; } public void addChangeListener(ChangeListener<E> listener) { this.listener = listener; } public E getValue() { return actualValue == null ? defaultValue : actualValue; } public synchronized void setValue(E value) { boolean changed = !getValue().equals(value); actualValue = value; if (listener != null && changed) listener.onChange(value);//only fire if there's a change } public synchronized void load(Properties p) { String prop = p.getProperty(nameInFile); setValue(prop != null ? Converter.convert(prop, type) : defaultValue); } public synchronized void save(Properties p) { //allows for temporary settings (i.e. stMuted) to not be saved if (!"".equals(nameInFile)) p.setProperty(nameInFile, getValue().toString()); } @Override public String toString() { return getValue().toString(); } /** * An event to be fired if the setting actually changed. * * @param <E> Rarely used, but the type that concords with the Setting. */ public interface ChangeListener<E> { void onChange(E value); } /** * Credit to http://balusc.blogspot.com/2007/08/generic-object-converter.html */ @SuppressWarnings("unused") private static class Converter { private static final Map<String, Method> CONVERTERS = new HashMap<>(); static { // Preload converters. Method[] methods = Converter.class.getDeclaredMethods(); for (Method method : methods) { if (method.getParameterTypes().length == 1) { // Converter should accept 1 argument. This skips the convert() method. CONVERTERS.put(method.getParameterTypes()[0].getName() + "_" + method.getReturnType().getName(), method); } } } /** * Convert the given object value to the given class. * * @param from The object value to be converted. * @param to The type class which the given object should be converted to. * @return The converted object value. */ public static <T> T convert(Object from, Class<T> to) { // Null is just null. if (from == null) { return null; } // Can we cast? Then just do so. if (to.isAssignableFrom(from.getClass())) { return to.cast(from); } // Lookup the suitable converter. String converterId = from.getClass().getName() + "_" + to.getName(); Method converter = CONVERTERS.get(converterId); if (converter != null) { // Convert the value. try { return to.cast(converter.invoke(to, from)); } catch (Exception e) { GUIMain.log("Cannot convert from " + from.getClass().getName() + " to " + to.getName() + ". Conversion failed with " + e.getMessage()); return null; } } else { GUIMain.log("Cannot convert setting! Converter not found for " + converterId); return null; } } //Converter Methods (used in Reflection) public static Boolean stringToBoolean(String value) { return Boolean.valueOf(value); } public static Integer stringToInteger(String value) { return Integer.valueOf(value); } public static Float stringToFloat(String value) { return Float.valueOf(value); } public static Font stringToFont(String value) { return Utils.stringToFont(value); } public static URL stringToURL(String value) throws MalformedURLException { return new URL(value); } } }