git-svn-id: https://samskivert.googlecode.com/svn/trunk@2043 6335cc39-0255-0410-8fd6-9bcaacd3b74c

This commit is contained in:
mdb
2007-01-31 00:41:25 +00:00
parent f8003a05c8
commit f406bc46c6
6 changed files with 470 additions and 491 deletions
@@ -31,32 +31,28 @@ import javax.swing.Scrollable;
import com.samskivert.Log; import com.samskivert.Log;
import com.samskivert.util.ComparableArrayList; import com.samskivert.util.ComparableArrayList;
import com.samskivert.util.Config; import com.samskivert.util.PrefsConfig;
import com.samskivert.util.DebugChords; import com.samskivert.util.DebugChords;
import com.samskivert.util.ListUtil; import com.samskivert.util.ListUtil;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
/** /**
* Provides a service where named variables can be registered as * Provides a service where named variables can be registered as adjustable by the developer at
* adjustable by the developer at runtime. Generally a special key is * runtime. Generally a special key is configured (via {@link DebugChords}) to pop up the
* configured (via {@link DebugChords}) to pop up the adjustable variables * adjustable variables interface which can then be used to toggle booleans, change integer values
* interface which can then be used to toggle booleans, change integer * and generally adjust debugging and tuning parameters. This is not meant to be an interface for
* values and generally adjust debugging and tuning parameters. This is * end-user configurable parameters, but to allow the developer to tweak runtime parameters more
* not meant to be an interface for end-user configurable parameters, but * easily.
* to allow the developer to tweak runtime parameters more easily.
* *
* <p> Adjustments are bound to a {@link Config} property which can be * <p> Adjustments are bound to a {@link PrefsConfig} property which can be changed through the
* changed through the config interface or through the bound runtime * config interface or through the bound runtime adjust.
* adjust.
* *
* <p> <em>Note:</em> adjusts are meant to be arranged in a two level * <p> <em>Note:</em> adjusts are meant to be arranged in a two level hierarchy. An adjust's name,
* hierarchy. An adjust's name, therefore, should be of the form: * therefore, should be of the form: <code>library.package.adjustment</code>. The
* <code>library.package.adjustment</code>. The <code>package</code> * <code>package</code> component can consist of multiple words joined with a period, but the
* component can consist of multiple words joined with a period, but the * <code>library</code> will always be the first word before the first period and the
* <code>library</code> will always be the first word before the first * <code>adjustment</code> always the last word after the final period. This is mainly only
* period and the <code>adjustment</code> always the last word after the * important for organizing the runtime adjustment editing interface.
* final period. This is mainly only important for organizing the runtime
* adjustment editing interface.
*/ */
public class RuntimeAdjust public class RuntimeAdjust
{ {
@@ -147,8 +143,7 @@ public class RuntimeAdjust
public static class BooleanAdjust extends Adjust public static class BooleanAdjust extends Adjust
implements ActionListener implements ActionListener
{ {
public BooleanAdjust (String descrip, String name, public BooleanAdjust (String descrip, String name, PrefsConfig config, boolean defval)
Config config, boolean defval)
{ {
super(descrip, name, config); super(descrip, name, config);
_value = _config.getValue(_name, defval); _value = _config.getValue(_name, defval);
@@ -197,8 +192,7 @@ public class RuntimeAdjust
/** Provides runtime adjustable integer variables. */ /** Provides runtime adjustable integer variables. */
public static class IntAdjust extends TextFieldAdjust public static class IntAdjust extends TextFieldAdjust
{ {
public IntAdjust (String descrip, String name, public IntAdjust (String descrip, String name, PrefsConfig config, int defval)
Config config, int defval)
{ {
super(descrip, name, config); super(descrip, name, config);
_value = _config.getValue(_name, defval); _value = _config.getValue(_name, defval);
@@ -251,8 +245,8 @@ public class RuntimeAdjust
public static class EnumAdjust extends Adjust public static class EnumAdjust extends Adjust
implements ActionListener implements ActionListener
{ {
public EnumAdjust (String descrip, String name, public EnumAdjust (String descrip, String name, PrefsConfig config,
Config config, String[] values, String defval) String[] values, String defval)
{ {
super(descrip, name, config); super(descrip, name, config);
_values = values; _values = values;
@@ -310,9 +304,8 @@ public class RuntimeAdjust
public static class FileAdjust extends Adjust public static class FileAdjust extends Adjust
implements ActionListener implements ActionListener
{ {
public FileAdjust (String descrip, String name, public FileAdjust (String descrip, String name, PrefsConfig config,
Config config, boolean directoriesOnly, boolean directoriesOnly, String defval)
String defval)
{ {
super(descrip, name, config); super(descrip, name, config);
_value = _config.getValue(_name, defval); _value = _config.getValue(_name, defval);
@@ -431,7 +424,7 @@ public class RuntimeAdjust
protected abstract static class TextFieldAdjust extends Adjust protected abstract static class TextFieldAdjust extends Adjust
implements ActionListener, FocusListener implements ActionListener, FocusListener
{ {
public TextFieldAdjust (String descrip, String name, Config config) public TextFieldAdjust (String descrip, String name, PrefsConfig config)
{ {
super(descrip, name, config); super(descrip, name, config);
} }
@@ -464,7 +457,7 @@ public class RuntimeAdjust
protected abstract static class Adjust protected abstract static class Adjust
implements PropertyChangeListener, Comparable<Adjust> implements PropertyChangeListener, Comparable<Adjust>
{ {
public Adjust (String descrip, String name, Config config) public Adjust (String descrip, String name, PrefsConfig config)
{ {
_name = name; _name = name;
_descrip = descrip; _descrip = descrip;
@@ -550,10 +543,9 @@ public class RuntimeAdjust
} }
protected String _name, _descrip; protected String _name, _descrip;
protected Config _config; protected PrefsConfig _config;
protected JPanel _editor; protected JPanel _editor;
} }
protected static ComparableArrayList<Adjust> _adjusts = protected static ComparableArrayList<Adjust> _adjusts = new ComparableArrayList<Adjust>();
new ComparableArrayList<Adjust>();
} }
@@ -9,7 +9,7 @@ import javax.swing.JComponent;
import javax.swing.JFrame; import javax.swing.JFrame;
import com.samskivert.swing.RuntimeAdjust; import com.samskivert.swing.RuntimeAdjust;
import com.samskivert.util.Config; import com.samskivert.util.PrefsConfig;
/** /**
* Does something extraordinary. * Does something extraordinary.
@@ -18,7 +18,7 @@ public class AdjustTestApp
{ {
public static void main (String[] args) public static void main (String[] args)
{ {
Config config = new Config("test"); PrefsConfig config = new PrefsConfig("test");
new RuntimeAdjust.IntAdjust( new RuntimeAdjust.IntAdjust(
"This is a test adjustment. It is nice.", "This is a test adjustment. It is nice.",
"samskivert.test.int_adjust1", config, 5); "samskivert.test.int_adjust1", config, 5);
@@ -16,7 +16,7 @@ import javax.swing.JToggleButton;
import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener; import javax.swing.event.AncestorListener;
import com.samskivert.util.Config; import com.samskivert.util.PrefsConfig;
import com.samskivert.util.IntListUtil; import com.samskivert.util.IntListUtil;
/** /**
@@ -25,8 +25,8 @@ import com.samskivert.util.IntListUtil;
public class ButtonUtil public class ButtonUtil
{ {
/** /**
* Set the specified button such that it alternates between being * Set the specified button such that it alternates between being selected and not whenever it
* selected and not whenever it is pushed. * is pushed.
*/ */
public static synchronized void setToggling (AbstractButton b) public static synchronized void setToggling (AbstractButton b)
{ {
@@ -44,28 +44,25 @@ public class ButtonUtil
} }
/** /**
* Binds the supplied button to the named boolean property in the * Binds the supplied button to the named boolean property in the supplied config repository.
* supplied config repository. When the button is pressed, it will * When the button is pressed, it will update the config property and when the config property
* update the config property and when the config property is changed * is changed (by the button or by other means) it will update the selected state of the
* (by the button or by other means) it will update the selected state * button. When the button is made non-visible, it will be unbound to the config's property and
* of the button. When the button is made non-visible, it will be * rebound again if it is once again visible.
* unbound to the config's property and rebound again if it is once
* again visible.
*/ */
public static void bindToProperty ( public static void bindToProperty (
String property, Config config, AbstractButton button, boolean defval) String property, PrefsConfig config, AbstractButton button, boolean defval)
{ {
// create a config binding which will take care of everything // create a config binding which will take care of everything
new ButtonConfigBinding(property, config, button, defval); new ButtonConfigBinding(property, config, button, defval);
} }
/** /**
* Configure the specified button to cause the specified * Configure the specified button to cause the specified property to cycle through the
* property to cycle through the specified values whenever the button is * specified values whenever the button is pressed.
* pressed.
*/ */
public static ActionListener cycleToProperty ( public static ActionListener cycleToProperty (
final String property, final Config config, AbstractButton button, final String property, final PrefsConfig config, AbstractButton button,
final int[] values) final int[] values)
{ {
ActionListener al = new ActionListener() { ActionListener al = new ActionListener() {
@@ -87,7 +84,7 @@ public class ButtonUtil
protected static class ButtonConfigBinding protected static class ButtonConfigBinding
implements AncestorListener, PropertyChangeListener, ItemListener implements AncestorListener, PropertyChangeListener, ItemListener
{ {
public ButtonConfigBinding (String property, Config config, public ButtonConfigBinding (String property, PrefsConfig config,
AbstractButton button, boolean defval) AbstractButton button, boolean defval)
{ {
_property = property; _property = property;
@@ -136,7 +133,7 @@ public class ButtonUtil
} }
protected String _property; protected String _property;
protected Config _config; protected PrefsConfig _config;
protected AbstractButton _button; protected AbstractButton _button;
protected boolean _defval; protected boolean _defval;
} }
+100 -434
View File
@@ -20,12 +20,6 @@
package com.samskivert.util; package com.samskivert.util;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
@@ -34,22 +28,16 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import java.util.prefs.AbstractPreferences;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import com.samskivert.Log; import com.samskivert.Log;
/** /**
* The config class provides a unified interaface to application * The config class provides a unified interaface to application configuration information. It
* configuration information. It takes care of loading properties files * takes care of loading properties files (done via the classpath) and allows for overriding and
* (done via the classpath) and allows for overriding and inheriting of * inheriting of properties values (see {@link ConfigUtil#loadInheritedProperties}).
* properties values (see {@link ConfigUtil#loadInheritedProperties}).
* *
* <p> A common pattern is to create, for each package that shares * <p> A common pattern is to create, for each package that shares configuration information, a
* configuration information, a singleton class containing a config object * singleton class containing a config object that is configured to load its data from a single
* that is configured to load its data from a single configuration * configuration file. For example:
* file. For example:
* *
* <pre> * <pre>
* public class FooConfig * public class FooConfig
@@ -60,9 +48,8 @@ import com.samskivert.Log;
* } * }
* </pre> * </pre>
* *
* which would look for <code>com/fribitz/foo.properties</code> in the * which would look for <code>com/fribitz/foo.properties</code> in the classpath and serve up those
* classpath and serve up those configuration values when requests were * configuration values when requests were made from <code>FooConfig.config</code>. For example:
* made from <code>FooConfig.config</code>. For example:
* *
* <pre> * <pre>
* int fiddles = FooConfig.config.getValue(FooConfig.FIDDLES, 0); * int fiddles = FooConfig.config.getValue(FooConfig.FIDDLES, 0);
@@ -71,8 +58,7 @@ import com.samskivert.Log;
* } * }
* </pre> * </pre>
* *
* An even better approach involves creating accessors for all defined * An even better approach involves creating accessors for all defined configuration properties:
* configuration properties:
* *
* <pre> * <pre>
* public class FooConfig * public class FooConfig
@@ -90,23 +76,16 @@ import com.samskivert.Log;
* } * }
* </pre> * </pre>
* *
* This allows the default value for <code>fiddles</code> to be specified * This allows the default value for <code>fiddles</code> to be specified in one place and
* in one place and simplifies life for the caller who can now simply * simplifies life for the caller who can now simply request <code>FooConfig.getFiddles()</code>.
* request <code>FooConfig.getFiddles()</code>.
* *
* <p> The config class allows one to override configuration values * @see PrefsConfig
* persistently, using the standard Java {@link Preferences} facilities to
* maintain the overridden values. If a property is {@link #setValue}d in
* a configuration object, it will remain overridden in between
* invocations of the application (leveraging the benefits of the
* pluggable preferences backends provided by the standard Java
* preferences facilities).
*/ */
public class Config public class Config
{ {
/** /**
* Constructs a new config object which will obtain configuration * Constructs a new config object which will obtain configuration information from the
* information from the specified properties bundle. * specified properties bundle.
*/ */
public Config (String path) public Config (String path)
{ {
@@ -121,119 +100,59 @@ public class Config
ConfigUtil.loadInheritedProperties(ppath, _props); ConfigUtil.loadInheritedProperties(ppath, _props);
} catch (FileNotFoundException fnfe) { } catch (FileNotFoundException fnfe) {
Log.debug("No properties file found to back config " + Log.debug("No properties file found to back config [path=" + path + "].");
"[path=" + path + "].");
} catch (IOException ioe) { } catch (IOException ioe) {
Log.warning("Unable to load configuration [path=" + path + Log.warning("Unable to load configuration [path=" + path + ", ioe=" + ioe + "].");
", ioe=" + ioe + "].");
}
// get a handle on the preferences instance that we'll use to
// override values in the properties file
try {
_prefs = Preferences.userRoot().node(path);
} catch (AccessControlException ace) {
// security manager won't let us access prefs, no problem!
Log.info("Can't access preferences [path=" + path + "].");
_prefs = new NullPreferences();
} }
} }
/** /**
* Constructs a config object which will obtain information from the * Constructs a config object which will obtain information from the supplied properties.
* supplied properties, rooted at the specified path in the
* preferences hieriarchy.
*/ */
public Config (final String path, Properties props) public Config (String path, Properties props)
{ {
_props = props; _props = props;
try {
_prefs = Preferences.userRoot().node(path);
} catch (AccessControlException ace) {
// security manager won't let us access prefs, no problem!
Log.info("Can't access preferences [path=" + path + "].");
_prefs = new NullPreferences();
}
} }
/** /**
* Fetches and returns the value for the specified configuration * Fetches and returns the value for the specified configuration property. If the value is not
* property. If the value is not specified in the associated * specified in the associated properties file, the supplied default value is returned instead.
* properties file, the supplied default value is returned instead. If * If the property specified in the file is poorly formatted (not and integer, not in proper
* the property specified in the file is poorly formatted (not and * array specification), a warning message will be logged and the default value will be
* integer, not in proper array specification), a warning message will * returned.
* be logged and the default value will be returned.
* *
* @param name name of the property. * @param name name of the property.
* @param defval the value to return if the property is not specified * @param defval the value to return if the property is not specified in the config file.
* in the config file.
* *
* @return the value of the requested property. * @return the value of the requested property.
*/ */
public int getValue (String name, int defval) public int getValue (String name, int defval)
{
return _prefs.getInt(name, getDefValue(name, defval));
}
/**
* Returns the value specified in the properties override file.
*/
protected int getDefValue (String name, int defval)
{ {
String val = _props.getProperty(name); String val = _props.getProperty(name);
if (val != null) { if (val != null) {
try { try {
// handles base 10, hex values, etc. return Integer.decode(val).intValue(); // handles base 10, hex values, etc.
return Integer.decode(val).intValue();
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
Log.warning("Malformed integer property [name=" + name + Log.warning("Malformed integer property [name=" + name + ", value=" + val + "].");
", value=" + val + "].");
} }
} }
return defval; return defval;
} }
/** /**
* Sets the value of the specified preference, overriding the value * Fetches and returns the value for the specified configuration property. If the value is not
* defined in the configuration files shipped with the application. * specified in the associated properties file, the supplied default value is returned instead.
*/ * If the property specified in the file is poorly formatted (not and integer, not in proper
public void setValue (String name, int value) * array specification), a warning message will be logged and the default value will be
{ * returned.
Integer oldValue = null;
if (_prefs.get(name, null) != null ||
_props.getProperty(name) != null) {
oldValue = Integer.valueOf(
_prefs.getInt(name, getDefValue(name, 0)));
}
_prefs.putInt(name, value);
_propsup.firePropertyChange(name, oldValue, Integer.valueOf(value));
}
/**
* Fetches and returns the value for the specified configuration
* property. If the value is not specified in the associated
* properties file, the supplied default value is returned instead. If
* the property specified in the file is poorly formatted (not and
* integer, not in proper array specification), a warning message will
* be logged and the default value will be returned.
* *
* @param name name of the property. * @param name name of the property.
* @param defval the value to return if the property is not specified * @param defval the value to return if the property is not specified in the config file.
* in the config file.
* *
* @return the value of the requested property. * @return the value of the requested property.
*/ */
public long getValue (String name, long defval) public long getValue (String name, long defval)
{
return _prefs.getLong(name, getDefValue(name, defval));
}
/**
* Returns the value specified in the properties override file.
*/
protected long getDefValue (String name, long defval)
{ {
String val = _props.getProperty(name); String val = _props.getProperty(name);
if (val != null) { if (val != null) {
@@ -248,45 +167,18 @@ public class Config
} }
/** /**
* Sets the value of the specified preference, overriding the value * Fetches and returns the value for the specified configuration property. If the value is not
* defined in the configuration files shipped with the application. * specified in the associated properties file, the supplied default value is returned instead.
*/ * If the property specified in the file is poorly formatted (not and integer, not in proper
public void setValue (String name, long value) * array specification), a warning message will be logged and the default value will be
{ * returned.
Long oldValue = null;
if (_prefs.get(name, null) != null ||
_props.getProperty(name) != null) {
oldValue = Long.valueOf(
_prefs.getLong(name, getDefValue(name, 0L)));
}
_prefs.putLong(name, value);
_propsup.firePropertyChange(name, oldValue, Long.valueOf(value));
}
/**
* Fetches and returns the value for the specified configuration
* property. If the value is not specified in the associated
* properties file, the supplied default value is returned instead. If
* the property specified in the file is poorly formatted (not and
* integer, not in proper array specification), a warning message will
* be logged and the default value will be returned.
* *
* @param name name of the property. * @param name name of the property.
* @param defval the value to return if the property is not specified * @param defval the value to return if the property is not specified in the config file.
* in the config file.
* *
* @return the value of the requested property. * @return the value of the requested property.
*/ */
public float getValue (String name, float defval) public float getValue (String name, float defval)
{
return _prefs.getFloat(name, getDefValue(name, defval));
}
/**
* Returns the value specified in the properties override file.
*/
protected float getDefValue (String name, float defval)
{ {
String val = _props.getProperty(name); String val = _props.getProperty(name);
if (val != null) { if (val != null) {
@@ -301,79 +193,17 @@ public class Config
} }
/** /**
* Sets the value of the specified preference, overriding the value * Fetches and returns the value for the specified configuration property. If the value is not
* defined in the configuration files shipped with the application. * specified in the associated properties file, the supplied default value is returned instead.
*/ * The returned value will be <code>false</code> if the config value is <code>"false"</code>
public void setValue (String name, float value) * (case-insensitive), else the return value will be true.
{
Float oldValue = null;
if (_prefs.get(name, null) != null ||
_props.getProperty(name) != null) {
oldValue = Float.valueOf(
_prefs.getFloat(name, getDefValue(name, 0f)));
}
_prefs.putFloat(name, value);
_propsup.firePropertyChange(name, oldValue, Float.valueOf(value));
}
/**
* Fetches and returns the value for the specified configuration
* property. If the value is not specified in the associated
* properties file, the supplied default value is returned instead.
* *
* @param name the name of the property to be fetched. * @param name the name of the property to be fetched.
* @param defval the value to return if the property is not specified * @param defval the value to return if the property is not specified in the config file.
* in the config file.
*
* @return the value of the requested property.
*/
public String getValue (String name, String defval)
{
// if there is a value, parse it into an integer
String val = _props.getProperty(name);
if (val != null) {
defval = val;
}
// finally look for an overridden value
return _prefs.get(name, defval);
}
/**
* Sets the value of the specified preference, overriding the value
* defined in the configuration files shipped with the application.
*/
public void setValue (String name, String value)
{
String oldValue = getValue(name, (String)null);
_prefs.put(name, value);
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Fetches and returns the value for the specified configuration
* property. If the value is not specified in the associated
* properties file, the supplied default value is returned
* instead. The returned value will be <code>false</code> if the
* config value is <code>"false"</code> (case-insensitive), else
* the return value will be true.
*
* @param name the name of the property to be fetched.
* @param defval the value to return if the property is not specified
* in the config file.
* *
* @return the value of the requested property. * @return the value of the requested property.
*/ */
public boolean getValue (String name, boolean defval) public boolean getValue (String name, boolean defval)
{
return _prefs.getBoolean(name, getDefValue(name, defval));
}
/**
* Returns the value specified in the properties override file.
*/
protected boolean getDefValue (String name, boolean defval)
{ {
String val = _props.getProperty(name); String val = _props.getProperty(name);
if (val != null) { if (val != null) {
@@ -383,41 +213,35 @@ public class Config
} }
/** /**
* Sets the value of the specified preference, overriding the value * Fetches and returns the value for the specified configuration property. If the value is not
* defined in the configuration files shipped with the application. * specified in the associated properties file, the supplied default value is returned instead.
*
* @param name the name of the property to be fetched.
* @param defval the value to return if the property is not specified in the config file.
*
* @return the value of the requested property.
*/ */
public void setValue (String name, boolean value) public String getValue (String name, String defval)
{ {
Boolean oldValue = null; return _props.getProperty(name, defval);
if (_prefs.get(name, null) != null ||
_props.getProperty(name) != null) {
oldValue = Boolean.valueOf(
_prefs.getBoolean(name, getDefValue(name, false)));
}
_prefs.putBoolean(name, value);
_propsup.firePropertyChange(name, oldValue, Boolean.valueOf(value));
} }
/** /**
* Fetches and returns the value for the specified configuration * Fetches and returns the value for the specified configuration property. If the value is not
* property. If the value is not specified in the associated * specified in the associated properties file, the supplied default value is returned instead.
* properties file, the supplied default value is returned instead. If * If the property specified in the file is poorly formatted (not and integer, not in proper
* the property specified in the file is poorly formatted (not and * array specification), a warning message will be logged and the default value will be
* integer, not in proper array specification), a warning message will * returned.
* be logged and the default value will be returned.
* *
* @param name the name of the property to be fetched. * @param name the name of the property to be fetched.
* @param defval the value to return if the property is not specified * @param defval the value to return if the property is not specified in the config file.
* in the config file.
* *
* @return the value of the requested property. * @return the value of the requested property.
*/ */
public int[] getValue (String name, int[] defval) public int[] getValue (String name, int[] defval)
{ {
// look up the value in the configuration file and use that to // look up the value in the configuration file and use that to look up any overridden value
// look up any overridden value String val = getValue(name, (String)null);
String val = _prefs.get(name, _props.getProperty(name));
int[] result = defval; int[] result = defval;
// parse it into an array of ints // parse it into an array of ints
@@ -434,35 +258,21 @@ public class Config
} }
/** /**
* Sets the value of the specified preference, overriding the value * Fetches and returns the value for the specified configuration property. If the value is not
* defined in the configuration files shipped with the application. * specified in the associated properties file, the supplied default value is returned instead.
*/ * If the property specified in the file is poorly formatted (not and integer, not in proper
public void setValue (String name, int[] value) * array specification), a warning message will be logged and the default value will be
{ * returned.
int[] oldValue = getValue(name, (int[])null);
_prefs.put(name, StringUtil.toString(value, "", ""));
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Fetches and returns the value for the specified configuration
* property. If the value is not specified in the associated
* properties file, the supplied default value is returned instead. If
* the property specified in the file is poorly formatted (not and
* integer, not in proper array specification), a warning message will
* be logged and the default value will be returned.
* *
* @param name the name of the property to be fetched. * @param name the name of the property to be fetched.
* @param defval the value to return if the property is not specified * @param defval the value to return if the property is not specified in the config file.
* in the config file.
* *
* @return the value of the requested property. * @return the value of the requested property.
*/ */
public long[] getValue (String name, long[] defval) public long[] getValue (String name, long[] defval)
{ {
// look up the value in the configuration file and use that to // look up the value in the configuration file and use that to look up any overridden value
// look up any overridden value String val = getValue(name, (String)null);
String val = _prefs.get(name, _props.getProperty(name));
long[] result = defval; long[] result = defval;
// parse it into an array of longs // parse it into an array of longs
@@ -479,35 +289,21 @@ public class Config
} }
/** /**
* Sets the value of the specified preference, overriding the value * Fetches and returns the value for the specified configuration property. If the value is not
* defined in the configuration files shipped with the application. * specified in the associated properties file, the supplied default value is returned instead.
*/ * If the property specified in the file is poorly formatted (not a floating point value, not
public void setValue (String name, long[] value) * in proper array specification), a warning message will be logged and the default value will
{ * be returned.
long[] oldValue = getValue(name, (long[])null);
_prefs.put(name, StringUtil.toString(value, "", ""));
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Fetches and returns the value for the specified configuration
* property. If the value is not specified in the associated
* properties file, the supplied default value is returned instead. If
* the property specified in the file is poorly formatted (not a
* floating point value, not in proper array specification), a warning
* message will be logged and the default value will be returned.
* *
* @param name the name of the property to be fetched. * @param name the name of the property to be fetched.
* @param defval the value to return if the property is not specified * @param defval the value to return if the property is not specified in the config file.
* in the config file.
* *
* @return the value of the requested property. * @return the value of the requested property.
*/ */
public float[] getValue (String name, float[] defval) public float[] getValue (String name, float[] defval)
{ {
// look up the value in the configuration file and use that to // look up the value in the configuration file and use that to look up any overridden value
// look up any overridden value String val = getValue(name, (String)null);
String val = _prefs.get(name, _props.getProperty(name));
float[] result = defval; float[] result = defval;
// parse it into an array of ints // parse it into an array of ints
@@ -524,35 +320,21 @@ public class Config
} }
/** /**
* Sets the value of the specified preference, overriding the value * Fetches and returns the value for the specified configuration property. If the value is not
* defined in the configuration files shipped with the application. * specified in the associated properties file, the supplied default value is returned instead.
*/ * If the property specified in the file is poorly formatted (not and integer, not in proper
public void setValue (String name, float[] value) * array specification), a warning message will be logged and the default value will be
{ * returned.
float[] oldValue = getValue(name, (float[])null);
_prefs.put(name, StringUtil.toString(value, "", ""));
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Fetches and returns the value for the specified configuration
* property. If the value is not specified in the associated
* properties file, the supplied default value is returned instead. If
* the property specified in the file is poorly formatted (not and
* integer, not in proper array specification), a warning message will
* be logged and the default value will be returned.
* *
* @param name the name of the property to be fetched. * @param name the name of the property to be fetched.
* @param defval the value to return if the property is not specified * @param defval the value to return if the property is not specified in the config file.
* in the config file.
* *
* @return the value of the requested property. * @return the value of the requested property.
*/ */
public String[] getValue (String name, String[] defval) public String[] getValue (String name, String[] defval)
{ {
// look up the value in the configuration file and use that to // look up the value in the configuration file and use that to look up any overridden value
// look up any overridden value String val = getValue(name, (String)null);
String val = _prefs.get(name, _props.getProperty(name));
String[] result = defval; String[] result = defval;
// parse it into an array of ints // parse it into an array of ints
@@ -569,78 +351,13 @@ public class Config
} }
/** /**
* Sets the value of the specified preference, overriding the value * Looks up the specified string-valued configuration entry, loads the class with that name and
* defined in the configuration files shipped with the application. * instantiates a new instance of that class, which is returned.
*/
public void setValue (String name, String[] value)
{
String[] oldValue = getValue(name, (String[])null);
_prefs.put(name, StringUtil.joinEscaped(value));
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Remove any set value for the specified preference.
*/
public void remove (String name)
{
// we treat the old value as a String, I hope that's ok!
String oldValue = getValue(name, (String) null);
_prefs.remove(name);
_propsup.firePropertyChange(name, oldValue, null);
}
/**
* Adds a listener that will be notified whenever any configuration
* properties are changed by a call to one of the <code>set</code>
* methods.
*/
public void addPropertyChangeListener (PropertyChangeListener listener)
{
_propsup.addPropertyChangeListener(listener);
}
/**
* Removes a property change listener previously added via a call to
* {@link #addPropertyChangeListener}.
*/
public void removePropertyChangeListener (PropertyChangeListener listener)
{
_propsup.removePropertyChangeListener(listener);
}
/**
* Adds a listener that will be notified whenever the specified
* configuration property is changed by a call to the appropriate
* <code>set</code> method.
*/
public void addPropertyChangeListener (
String name, PropertyChangeListener listener)
{
_propsup.addPropertyChangeListener(name, listener);
}
/**
* Removes a property change listener previously added via a call to
* {@link #addPropertyChangeListener(String,PropertyChangeListener)}.
*/
public void removePropertyChangeListener (
String name, PropertyChangeListener listener)
{
_propsup.removePropertyChangeListener(name, listener);
}
/**
* Looks up the specified string-valued configuration entry, loads the
* class with that name and instantiates a new instance of that class,
* which is returned.
* *
* @param name the name of the property to be fetched. * @param name the name of the property to be fetched.
* @param defcname the class name to use if the property is not * @param defcname the class name to use if the property is not specified in the config file.
* specified in the config file.
* *
* @exception Exception thrown if any error occurs while loading or * @exception Exception thrown if any error occurs while loading or instantiating the class.
* instantiating the class.
*/ */
public Object instantiateValue (String name, String defcname) public Object instantiateValue (String name, String defcname)
throws Exception throws Exception
@@ -649,10 +366,9 @@ public class Config
} }
/** /**
* Returns a properties object containing all configuration values * Returns a properties object containing all configuration values that start with the supplied
* that start with the supplied prefix (plus a trailing "." which will * prefix (plus a trailing "." which will be added if it doesn't already exist). The keys in
* be added if it doesn't already exist). The keys in the * the sub-properties will have had the prefix stripped off.
* sub-properties will have had the prefix stripped off.
*/ */
public Properties getSubProperties (String prefix) public Properties getSubProperties (String prefix)
{ {
@@ -662,10 +378,9 @@ public class Config
} }
/** /**
* Fills into the supplied properties object all configuration values * Fills into the supplied properties object all configuration values that start with the
* that start with the supplied prefix (plus a trailing "." which will * supplied prefix (plus a trailing "." which will be added if it doesn't already exist). The
* be added if it doesn't already exist). The keys in the * keys in the sub-properties will have had the prefix stripped off.
* sub-properties will have had the prefix stripped off.
*/ */
public void getSubProperties (String prefix, Properties target) public void getSubProperties (String prefix, Properties target)
{ {
@@ -690,75 +405,26 @@ public class Config
} }
/** /**
* Returns an iterator that returns all of the configuration keys in * Returns an iterator that returns all of the configuration keys in this config object.
* this config object.
*/ */
public Iterator<String> keys () public Iterator<String> keys ()
{ {
// what with all the complicated business, we just need to take
// the brute force approach and enumerate everything up front
HashSet<String> matches = new HashSet<String>(); HashSet<String> matches = new HashSet<String>();
enumerateKeys(matches);
// add the keys provided in the config files
Enumeration defkeys = _props.propertyNames();
while (defkeys.hasMoreElements()) {
matches.add((String)defkeys.nextElement());
}
// then add the overridden keys
try {
String[] keys = _prefs.keys();
for (int i = 0; i < keys.length; i++) {
matches.add(keys[i]);
}
} catch (BackingStoreException bse) {
Log.warning("Unable to enumerate preferences keys " +
"[error=" + bse + "].");
}
return matches.iterator(); return matches.iterator();
} }
/** This is used if we don't have security access to the preferences protected void enumerateKeys (HashSet<String> keys)
* implementation that we'd like. */
protected static class NullPreferences extends AbstractPreferences
{ {
public NullPreferences () { Enumeration defkeys = _props.propertyNames();
super(null, ""); while (defkeys.hasMoreElements()) {
} keys.add((String)defkeys.nextElement());
protected void putSpi (String key, String value) {
}
protected String getSpi (String key) {
return null;
}
protected void removeSpi (String key) {
}
protected void removeNodeSpi () throws BackingStoreException {
}
protected String[] keysSpi () throws BackingStoreException {
return new String[0];
}
protected String[] childrenNamesSpi () throws BackingStoreException {
return new String[0];
}
protected AbstractPreferences childSpi (String name) {
return null;
}
protected void syncSpi () throws BackingStoreException {
}
protected void flushSpi () throws BackingStoreException {
} }
} }
/** Contains the default configuration information. */ /** Contains the default configuration information. */
protected Properties _props; protected Properties _props;
/** Used to maintain configuration overrides. */
protected Preferences _prefs;
/** Used to support our property change mechanism. */
protected PropertyChangeSupport _propsup = new PropertyChangeSupport(this);
/** The file extension used for configuration files. */ /** The file extension used for configuration files. */
protected static final String PROPS_SUFFIX = ".properties"; protected static final String PROPS_SUFFIX = ".properties";
} }
@@ -0,0 +1,325 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2007 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.util;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.prefs.AbstractPreferences;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import com.samskivert.Log;
/**
* Extends the {@link Config} mechanism to allow the modification of configuration values, which
* are persisted using the Java preferences system. If a property is {@link #setValue}d, it will
* remain overridden in between invocations of the application (leveraging the benefits of the
* pluggable preferences backends provided by the standard Java preferences facilities).
*/
public class PrefsConfig extends Config
{
/**
* Constructs a new config object which will obtain configuration information from the
* specified properties bundle.
*/
public PrefsConfig (String path)
{
super(path);
// get a handle on the preferences instance that we'll use to override values in the
// properties file
try {
_prefs = Preferences.userRoot().node(path);
} catch (AccessControlException ace) {
// security manager won't let us access prefs, no problem!
Log.info("Can't access preferences [path=" + path + "].");
_prefs = new NullPreferences();
}
}
/**
* Constructs a config object which will obtain information from the supplied properties,
* rooted at the specified path in the preferences hieriarchy.
*/
public PrefsConfig (String path, Properties props)
{
super(path, props);
try {
_prefs = Preferences.userRoot().node(path);
} catch (AccessControlException ace) {
// security manager won't let us access prefs, no problem!
Log.info("Can't access preferences [path=" + path + "].");
_prefs = new NullPreferences();
}
}
/**
* Sets the value of the specified preference, overriding the value defined in the
* configuration files shipped with the application.
*/
public void setValue (String name, int value)
{
Integer oldValue = null;
if (_prefs.get(name, null) != null || _props.getProperty(name) != null) {
oldValue = Integer.valueOf(_prefs.getInt(name, super.getValue(name, 0)));
}
_prefs.putInt(name, value);
_propsup.firePropertyChange(name, oldValue, Integer.valueOf(value));
}
/**
* Sets the value of the specified preference, overriding the value defined in the
* configuration files shipped with the application.
*/
public void setValue (String name, long value)
{
Long oldValue = null;
if (_prefs.get(name, null) != null || _props.getProperty(name) != null) {
oldValue = Long.valueOf(_prefs.getLong(name, super.getValue(name, 0L)));
}
_prefs.putLong(name, value);
_propsup.firePropertyChange(name, oldValue, Long.valueOf(value));
}
/**
* Sets the value of the specified preference, overriding the value defined in the
* configuration files shipped with the application.
*/
public void setValue (String name, float value)
{
Float oldValue = null;
if (_prefs.get(name, null) != null || _props.getProperty(name) != null) {
oldValue = Float.valueOf(_prefs.getFloat(name, super.getValue(name, 0f)));
}
_prefs.putFloat(name, value);
_propsup.firePropertyChange(name, oldValue, Float.valueOf(value));
}
/**
* Sets the value of the specified preference, overriding the value defined in the
* configuration files shipped with the application.
*/
public void setValue (String name, boolean value)
{
Boolean oldValue = null;
if (_prefs.get(name, null) != null || _props.getProperty(name) != null) {
oldValue = Boolean.valueOf(_prefs.getBoolean(name, super.getValue(name, false)));
}
_prefs.putBoolean(name, value);
_propsup.firePropertyChange(name, oldValue, Boolean.valueOf(value));
}
/**
* Sets the value of the specified preference, overriding the value defined in the
* configuration files shipped with the application.
*/
public void setValue (String name, String value)
{
String oldValue = getValue(name, (String)null);
_prefs.put(name, value);
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Sets the value of the specified preference, overriding the value defined in the
* configuration files shipped with the application.
*/
public void setValue (String name, int[] value)
{
int[] oldValue = getValue(name, (int[])null);
_prefs.put(name, StringUtil.toString(value, "", ""));
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Sets the value of the specified preference, overriding the value defined in the
* configuration files shipped with the application.
*/
public void setValue (String name, long[] value)
{
long[] oldValue = getValue(name, (long[])null);
_prefs.put(name, StringUtil.toString(value, "", ""));
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Sets the value of the specified preference, overriding the value defined in the
* configuration files shipped with the application.
*/
public void setValue (String name, float[] value)
{
float[] oldValue = getValue(name, (float[])null);
_prefs.put(name, StringUtil.toString(value, "", ""));
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Sets the value of the specified preference, overriding the value defined in the
* configuration files shipped with the application.
*/
public void setValue (String name, String[] value)
{
String[] oldValue = getValue(name, (String[])null);
_prefs.put(name, StringUtil.joinEscaped(value));
_propsup.firePropertyChange(name, oldValue, value);
}
/**
* Remove any set value for the specified preference.
*/
public void remove (String name)
{
// we treat the old value as a String, I hope that's ok!
String oldValue = getValue(name, (String) null);
_prefs.remove(name);
_propsup.firePropertyChange(name, oldValue, null);
}
/**
* Adds a listener that will be notified whenever any configuration properties are changed by a
* call to one of the <code>set</code> methods.
*/
public void addPropertyChangeListener (PropertyChangeListener listener)
{
_propsup.addPropertyChangeListener(listener);
}
/**
* Removes a property change listener previously added via a call to {@link
* #addPropertyChangeListener}.
*/
public void removePropertyChangeListener (PropertyChangeListener listener)
{
_propsup.removePropertyChangeListener(listener);
}
/**
* Adds a listener that will be notified whenever the specified configuration property is
* changed by a call to the appropriate <code>set</code> method.
*/
public void addPropertyChangeListener (String name, PropertyChangeListener listener)
{
_propsup.addPropertyChangeListener(name, listener);
}
/**
* Removes a property change listener previously added via a call to {@link
* #addPropertyChangeListener(String,PropertyChangeListener)}.
*/
public void removePropertyChangeListener (String name, PropertyChangeListener listener)
{
_propsup.removePropertyChangeListener(name, listener);
}
@Override // from Config
public int getValue (String name, int defval)
{
return _prefs.getInt(name, super.getValue(name, defval));
}
@Override // from Config
public long getValue (String name, long defval)
{
return _prefs.getLong(name, super.getValue(name, defval));
}
@Override // from Config
public float getValue (String name, float defval)
{
return _prefs.getFloat(name, super.getValue(name, defval));
}
@Override // from Config
public boolean getValue (String name, boolean defval)
{
return _prefs.getBoolean(name, super.getValue(name, defval));
}
@Override // from Config
public String getValue (String name, String defval)
{
return _prefs.get(name, super.getValue(name, defval));
}
@Override // from Config
protected void enumerateKeys (HashSet<String> keys)
{
super.enumerateKeys(keys);
try {
for (String key : _prefs.keys()) {
keys.add(key);
}
} catch (BackingStoreException bse) {
Log.warning("Unable to enumerate preferences keys [error=" + bse + "].");
}
}
/** This is used if we don't have security access to the preferences
* implementation that we'd like. */
protected static class NullPreferences extends AbstractPreferences
{
public NullPreferences () {
super(null, "");
}
protected void putSpi (String key, String value) {
}
protected String getSpi (String key) {
return null;
}
protected void removeSpi (String key) {
}
protected void removeNodeSpi () throws BackingStoreException {
}
protected String[] keysSpi () throws BackingStoreException {
return new String[0];
}
protected String[] childrenNamesSpi () throws BackingStoreException {
return new String[0];
}
protected AbstractPreferences childSpi (String name) {
return null;
}
protected void syncSpi () throws BackingStoreException {
}
protected void flushSpi () throws BackingStoreException {
}
}
/** Used to maintain configuration overrides. */
protected Preferences _prefs;
/** Used to support our property change mechanism. */
protected PropertyChangeSupport _propsup = new PropertyChangeSupport(this);
}
@@ -9,7 +9,7 @@ import java.util.Properties;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestCase; import junit.framework.TestCase;
import com.samskivert.util.Config; import com.samskivert.util.PrefsConfig;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
/** /**
@@ -24,7 +24,7 @@ public class ConfigTest extends TestCase
public void runTest () public void runTest ()
{ {
Config config = new Config("rsrc/util/test"); PrefsConfig config = new PrefsConfig("rsrc/util/test");
System.out.println("prop1: " + config.getValue("prop1", 1)); System.out.println("prop1: " + config.getValue("prop1", 1));
System.out.println("prop2: " + config.getValue("prop2", "two")); System.out.println("prop2: " + config.getValue("prop2", "two"));
@@ -51,8 +51,7 @@ public class ConfigTest extends TestCase
config.setValue("sub.sub3", "three"); config.setValue("sub.sub3", "three");
Properties subprops = config.getSubProperties("sub"); Properties subprops = config.getSubProperties("sub");
System.out.println("Sub: " + System.out.println("Sub: " + StringUtil.toString(subprops.propertyNames()));
StringUtil.toString(subprops.propertyNames()));
} }
public static Test suite () public static Test suite ()