/** * */ package ecologylab.appframework.types.prefs; //import java.awt.Color; import java.io.File; import java.util.LinkedList; import ecologylab.appframework.SingletonApplicationEnvironment; import ecologylab.collections.Scope; import ecologylab.platformspecifics.FundamentalPlatformSpecifics; import ecologylab.serialization.ElementState; import ecologylab.serialization.TranslationContext; import ecologylab.serialization.annotations.simpl_inherit; import ecologylab.serialization.annotations.simpl_scalar; import ecologylab.serialization.types.element.IMappable; /** * Generic base class for application Preference objects. * * @author andruid */ @simpl_inherit public abstract class Pref<T> extends ElementState implements IMappable<String>, Cloneable { /** The global registry of Pref objects. Used for providing lookup services. */ static final Scope<Pref<?>> allPrefsMap = new Scope<Pref<?>>(); /** The ApplicationEnvironment associated with this JVM. */ static final SingletonApplicationEnvironment aE = null; /** Name of a Pref; provides index into the preferences map. */ @simpl_scalar protected String name; /** Cached value */ T valueCached; /** * The list of PrefChangedListeners registered to respond to changes in Prefs. */ static LinkedList<PrefChangedListener> listeners = new LinkedList<PrefChangedListener>(); /** No-argument constructor for XML translation. */ public Pref() { super(); } protected Pref(String name) { this.name = name; } /** * Public generic accessor for the value. Caches autoboxed values, for efficiency. * * @return */ public T value() { T result = valueCached; if (result == null) { result = getValue(); valueCached = result; } return result; } /** * Print Pref name and value */ public void print() { println("Pref: name: " + name + ", value: " + this.getValue()); } /** * Return String of Pref name and value * * @return String of Pref name and value */ @Override public String toString() { return "Pref: " + name + "(" + this.getValue() + ")"; } /** * Generic get value returns the value as the actual type you want. This version should only be * called by value(), so that autoboxed types can be cached. This method *does not* do the * caching. * * @return */ protected abstract T getValue(); /** * Generic value setter. Uses boxed reference objects for primitives, which are a bit extra * expensive. * * @param newValue */ public abstract void setValue(T newValue); /** * Performs all housekeeping associated with updating this Pref. prefUpdated() should be called * whenever the value of this has been changed. * * Notifies all listeners that the pref's value has changed. * * Set valueCached to null */ protected void prefChanged() { valueCached = null; Pref.firePrefChangedEvent(this); } /** * This is for working with <code>Pref</code>s whose values you will continue to access as they * are edited, live, by the user. The result will be immediate changes in the program's behavior. * <p/> * Lookup a Pref associated with name. If you find it return it. If not, create a new Pref object * of the correct type. Set its value to default value. * * @param name * Name of the Pref to lookup and find or create. * @param defaultValue * Initial value of the Pref if it didn't already exist. * * @return A usable Pref object associated with name, either from the registry or newly created */ public static PrefBoolean usePrefBoolean(String name, boolean defaultValue) { PrefBoolean pref = (PrefBoolean) lookupPref(name); if (pref == null) { pref = new PrefBoolean(defaultValue); pref.name = name; pref.register(); } return pref; } /** * This is for working with <code>Pref</code>s whose values you will continue to access as they * are edited, live, by the user. The result will be immediate changes in the program's behavior. * <p/> * Lookup a Pref associated with name. If you find it return it. If not, create a new Pref object * of the correct type. Set its value to default value. * * @param name * Name of the Pref to lookup and find or create. * @param defaultValue * Initial value of the Pref if it didn't already exist. * * @return A usable Pref object associated with name, either from the registry or newly created */ public static PrefFloat usePrefFloat(String name, float defaultValue) { PrefFloat pref = (PrefFloat) lookupPref(name); if (pref == null) { pref = new PrefFloat(defaultValue); pref.name = name; pref.register(); } return pref; } /** * This is for working with <code>Pref</code>s whose values you will continue to access as they * are edited, live, by the user. The result will be immediate changes in the program's behavior. * <p/> * Lookup a Pref associated with name. If you find it return it. If not, create a new Pref object * of the correct type. Set its value to default value. * * @param name * Name of the Pref to lookup and find or create. * @param defaultValue * Initial value of the Pref if it didn't already exist. * * @return A usable Pref object associated with name, either from the registry or newly created */ public static PrefString usePrefString(String name, String defaultValue) { PrefString pref = (PrefString) lookupPref(name); if (pref == null) { pref = new PrefString(defaultValue); pref.name = name; pref.register(); } return pref; } /** * This is for working with <code>Pref</code>s whose values you will continue to access as they * are edited, live, by the user. The result will be immediate changes in the program's behavior. * <p/> * Lookup a Pref associated with name. If you find it return it. If not, create a new Pref object * of the correct type. Set its value to default value. * * @param name * Name of the Pref to lookup and find or create. * @param defaultValue * Initial value of the Pref if it didn't already exist. * * @return A usable Pref object associated with name, either from the registry or newly created */ public static PrefInt usePrefInt(String name, int defaultValue) { PrefInt pref = (PrefInt) lookupPref(name); if (pref == null) { pref = new PrefInt(defaultValue); pref.name = name; pref.register(); } return pref; } /** * Lookup a Pref associated with name. If you find it, it is the operative Pref If not, create a * new Pref object of the correct type, and register it. * <p/> * Set the value of the operative Pref to that passed in here. * * @param name * Name of the Pref to lookup and find or create. * @param defaultValue * Initial value of the Pref if it didn't already exist. */ public static void useAndSetPrefInt(String name, int value) { PrefInt thatPrefInt = usePrefInt(name, value); thatPrefInt.setValue(value); } public static void useAndSetPrefEnum(String name, Enum value) { PrefEnum thatPrefEnum = usePrefEnum(name, value); thatPrefEnum.setValue(value); } /** * This is for working with <code>Pref</code>s whose values you will continue to access as they * are edited, live, by the user. The result will be immediate changes in the program's behavior. * <p/> * Lookup a Pref associated with name. If you find it return it. If not, create a new Pref object * of the correct type. Set its value to default value. * * @param name * Name of the Pref to lookup and find or create. * @param defaultValue * Initial value of the Pref if it didn't already exist. * * @return A usable Pref object associated with name, either from the registry or newly created */ public static Object usePrefColor(String name, Object defaultValue) { return FundamentalPlatformSpecifics.get().usePrefColor(name, defaultValue); } /** * This is for working with <code>Pref</code>s whose values you will continue to access as they * are edited, live, by the user. The result will be immediate changes in the program's behavior. * <p/> * Lookup a Pref associated with name. If you find it return it. If not, create a new Pref object * of the correct type. Set its value to default value. * * @param name * Name of the Pref to lookup and find or create. * @param defaultValue * Initial value of the Pref if it didn't already exist. * * @return A usable Pref object associated with name, either from the registry or newly created */ public static PrefEnum usePrefEnum(String name, Enum defaultValue) { PrefEnum pref = (PrefEnum) lookupPref(name); if (pref == null) { pref = new PrefEnum(defaultValue); pref.name = name; pref.register(); } return pref; } /** * Look up a Pref by name in the map of all Prefs * * @param name * Name of Pref * * @return Pref with the given name */ public static Pref<?> lookupPref(String name) { Pref<?> pref = allPrefsMap.get(name); return pref; } public static void printAllPrefsMap() { for (Pref p : allPrefsMap.values()) { System.out.println("PREF: " + p.getName() + " = " + p.value()); } } /** * Turn the entry for a pref name to null in the global map. * * @param name */ public static void clearPref(String name) { allPrefsMap.remove(name); } /** * Look up a PrefInt by name in the map of all Prefs. Return defaultValue if PrefInt's value is * null. * * @param name * Name of PrefInt * @param defaultValue * default value for PrefInt * * @return PrefInt's value or default value if doesn't exist */ public static int lookupInt(String name, int defaultValue) throws ClassCastException { /* * could do this -- its heavier weight -- create a Pref if there wasn't one, and set its default * value as here PrefInt prefInt = usePrefInt(name, defaultValue); return prefInt.value(); */ PrefInt prefInt = ((PrefInt) lookupPref(name)); return (prefInt == null) ? defaultValue : prefInt.value(); } /** * Look up a PrefInt by name in the map of all Prefs. * * @param name * Name of PrefInt * * @return PrefInt's value or 0 */ public static int lookupInt(String name) throws ClassCastException { return lookupInt(name, 0); } /** * Look up a PrefLong by name in the map of all Prefs. Return defaultValue if PrefInt's value is * null. * * @param name * Name of PrefLong * @param defaultValue * default value for PrefLong * * @return PrefInt's value or default value if doesn't exist */ public static long lookupLong(String name, long defaultValue) throws ClassCastException { PrefLong prefLong = ((PrefLong) lookupPref(name)); return (prefLong == null) ? defaultValue : prefLong.value(); } /** * Look up a PrefLong by name in the map of all Prefs. * * @param name * Name of PrefLong * * @return PrefInt's value or 0 */ public static long lookupLong(String name) throws ClassCastException { return lookupInt(name, 0); } /** * Look up a PrefBoolean by name in the map of all Prefs. Return defaultValue if PrefBoolean's * value is null. * * @param name * Name of PrefBoolean * @param defaultValue * default value for PrefBoolean * * @return PrefBoolean's value or default value if doesn't exist */ public static boolean lookupBoolean(String name, boolean defaultValue) throws ClassCastException { PrefBoolean prefBoolean = ((PrefBoolean) lookupPref(name)); return (prefBoolean == null) ? defaultValue : prefBoolean.value(); } /** * Look up a PrefBoolean by name in the map of all Prefs. * * @param name * Name of PrefBoolean * * @return PrefBoolean's value or false if doesn't exist */ public static boolean lookupBoolean(String name) throws ClassCastException { return lookupBoolean(name, false); } /** * Look up a PrefFloat by name in the map of all Prefs. Return defaultValue if PrefFloat's value * is null. * * @param name * Name of PrefFloat * @param defaultValue * default value to set PrefFloat to * * @return PrefFloat's value or default value if doesn't exist */ public static float lookupFloat(String name, float defaultValue) throws ClassCastException { PrefFloat prefFloat = ((PrefFloat) lookupPref(name)); return (prefFloat == null) ? defaultValue : prefFloat.value(); } /** * Look up a PrefFloat by name in the map of all Prefs. * * @param name * Name of PrefFloat * * @return PrefFloat's value (if exists) or 1.0f */ public static float lookupFloat(String name) throws ClassCastException { return lookupFloat(name, 1.0f); } /** * Look up a PrefDouble by name in the map of all Prefs. Return defaultValue if PrefDouble's value * is null. * * @param name * Name of PrefDouble * @param defaultValue * default value to set PrefDouble to * * @return PrefDouble's value or default value if doesn't exist */ public static double lookupDouble(String name, double defaultValue) throws ClassCastException { PrefDouble prefDouble = ((PrefDouble) lookupPref(name)); return (prefDouble == null) ? defaultValue : prefDouble.value(); } /** * Look up a PrefDouble by name in the map of all Prefs. * * @param name * Name of PrefDouble * * @return PrefDouble's value (if exists) or 1.0 */ public static double lookupDouble(String name) throws ClassCastException { return lookupDouble(name, 1.0); } /** * Look up a PrefString by name in the map of all Prefs. Return defaultValue if PrefString's value * is null. * * @param name * Name of PrefString * @param defaultValue * default value for PrefString * * @return PrefString's value or default value if doesn't exist */ public static String lookupString(String name, String defaultValue) throws ClassCastException { PrefString prefString = ((PrefString) lookupPref(name)); return (prefString == null) ? defaultValue : prefString.value(); } /** * Look up a PrefString by name in the map of all Prefs. Return null if PrefString's value is * null. * * @param name * Name of PrefString * * @return PrefString's value or null */ public static String lookupString(String name) throws ClassCastException { return lookupString(name, null); } /** * Look up a PrefFile by name in the map of all Prefs. Return null if the PrefFile's value is * null; * * @param name * Name of the PrefFile * @return PrefFile's value or null, if the Pref associated with name does not exist * @throws ClassCastException * if name does not match a PrefFile object */ public static File lookupFile(String name) throws ClassCastException { PrefFile prefFile = ((PrefFile) lookupPref(name)); return (prefFile == null) ? null : prefFile.value(); } /** * Look up a PrefColor by name in the map of all Prefs. Return defaultValue if PrefColor's value * is null. * * @param name * Name of PrefColor * @param defaultValue * default value for PrefColor * * @return PrefColor's value or default value if doesn't exist */ public static Object lookupColor(String name, Object defaultValue) { return FundamentalPlatformSpecifics.get().lookupColor(name, defaultValue); } /** * Look up a PrefColor by name in the map of all Prefs. * * @param name * Name of PrefColor * * @return PrefColor's value or null */ public static Object lookupColor(String name) throws ClassCastException { return lookupColor(name, null); } /** * Look up a PrefElementState by name in the map of all Prefs. Set to defaultValue if * PrefElementState's value is null. * * @param name * Name of PrefElementState * * @return PrefElementState's value or null if doesn't exist */ public static ElementState lookupElementState(String name) throws ClassCastException { PrefElementState prefElementState = ((PrefElementState) lookupPref(name)); return (ElementState) ((prefElementState == null) ? null : prefElementState.value()); } public static Enum lookupEnum(String name, Enum defaultValue) throws ClassCastException { PrefEnum prefEnum = ((PrefEnum) lookupPref(name)); return (prefEnum == null) ? defaultValue : prefEnum.value(); } /** * Check for existence / membership. * * @param key * * @return true if there is a Pref already registered with name key */ public static boolean hasPref(String name) { return allPrefsMap.containsKey(name); } /** * Create an entry for this in the allPrefsMap. * */ void register() { allPrefsMap.put(this.name, this); } /** * Check for existence / membership. * * @param key * * @return true if there is a Pref already registered with name key */ public static boolean containsKey(String key) { return allPrefsMap.containsKey(key); } public String getName() { return name; } public static void addPrefChangedListener(PrefChangedListener l) { listeners.add(l); } private static void firePrefChangedEvent(Pref<?> pref) { for (PrefChangedListener l : listeners) { l.prefChanged(pref); } } public static void prefUpdated(Pref<?> pref) { firePrefChangedEvent(pref); } /** * @see ecologylab.serialization.types.element.IMappable#key() */ @Override public String key() { return name; } /** * @see ecologylab.serialization.types.element.ArrayListState#clone() This clone method is * REQUIRED for preferences being maintained by a servlet. The specific case that we have in * place (dec '09) that uses this is the Studies framework. The clone functionality enables * maintaining a preference set for each user in a user study. */ @Override public abstract Pref<T> clone(); /** * This method can be called after the pref has translated, with the current session scope. * * @param scope */ public void postLoadHook(Scope scope) { } @Override public void deserializationPostHook(TranslationContext translationContext, Object object) { if (object instanceof Pref<?>) { Pref<?> pref = (Pref<?>) object; pref.register(); } } }