diff --git a/projects/samskivert/build.xml b/projects/samskivert/build.xml
index 3071e228..c28e4042 100644
--- a/projects/samskivert/build.xml
+++ b/projects/samskivert/build.xml
@@ -15,6 +15,9 @@
An application should construct a single instance of
+ * Config and use it to access all of its configuration
+ * information.
+ */
+public class Config
+{
+ /**
+ * Constructs a new config object which can be used immediately by
+ * binding properties files into the namespace and subsequently
+ * requesting values.
+ */
+ public Config ()
+ {
+ }
+
+ /**
+ * Binds the specified properties file into the namespace with the
+ * specified name. If the properties file in question contains a
+ * property of the name foo.bar and the file is bound
+ * into the namespace under baz, then that property would
+ * be accessed as baz.foo.bar.
+ *
+ * @param name the root name for all properties in this file.
+ * @param path the path to the properties file which must live
+ * somewhere in the classpath. For example: foo/bar/baz
+ * would indicate a file named "foo/bar/baz.properties" living in the
+ * classpath.
+ *
+ * @exception IOException thrown if an error occurrs loading the
+ * properties file (like it doesn't exist or cannot be accessed).
+ */
+ public void bindProperties (String name, String path)
+ throws IOException
+ {
+ // append the file suffix onto the path
+ path += PROPS_SUFFIX;
+ // load the properties file
+ Properties props = ConfigUtil.loadProperties(path);
+ if (props == null) {
+ throw new IOException("Unable to load properties file: " + path);
+ }
+ // put it into the hashtable with the specified name
+ _props.put(name, props);
+ }
+
+ /**
+ * 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 the fully qualified name of the property (fully qualified
+ * meaning that it contains the namespace identifier as well), for
+ * example: foo.bar.baz.
+ * @param defval the value to return if the property is not specified
+ * in the config file.
+ *
+ * @return the value of the requested property.
+ */
+ public int getValue (String name, int defval)
+ {
+ String val = resolveProperty(name);
+
+ // if it's not specified, we return the default
+ if (val == null) {
+ return defval;
+ }
+
+ // otherwise parse it into an integer
+ try {
+ return Integer.parseInt(val);
+ } catch (NumberFormatException nfe) {
+ Log.warning("Malformed integer property [fqn=" + name +
+ ", value=" + val + "].");
+ return defval;
+ }
+ }
+
+ /**
+ * 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 the fully qualified name of the property (fully qualified
+ * meaning that it contains the namespace identifier as well), for
+ * example: foo.bar.baz.
+ * @param defval the value to return if the property is not specified
+ * in the config file.
+ *
+ * @return the value of the requested property.
+ */
+ public String getValue (String name, String defval)
+ {
+ String val = resolveProperty(name);
+ // if it's not specified, we return the default
+ return (val == null) ? defval : val;
+ }
+
+ /**
+ * 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 the fully qualified name of the property (fully qualified
+ * meaning that it contains the namespace identifier as well), for
+ * example: foo.bar.baz.
+ * @param defval the value to return if the property is not specified
+ * in the config file.
+ *
+ * @return the value of the requested property.
+ */
+ public int[] getValue (String name, int[] defval)
+ {
+ String val = resolveProperty(name);
+
+ // if it's not specified, we return the default
+ if (val == null) {
+ return defval;
+ }
+
+ // otherwise parse it into an array of ints
+ int[] result = StringUtil.parseIntArray(val);
+ if (result == null) {
+ Log.warning("Malformed int array property [fqn=" + name +
+ ", value=" + val + "].");
+ return defval;
+ }
+
+ return result;
+ }
+
+ /**
+ * 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 the fully qualified name of the property (fully qualified
+ * meaning that it contains the namespace identifier as well), for
+ * example: foo.bar.baz.
+ * @param defval the value to return if the property is not specified
+ * in the config file.
+ *
+ * @return the value of the requested property.
+ */
+ public String[] getValue (String name, String[] defval)
+ {
+ String val = resolveProperty(name);
+
+ // if it's not specified, we return the default
+ if (val == null) {
+ return defval;
+ }
+
+ // otherwise parse it into an array of strings
+ String[] result = StringUtil.parseStringArray(val);
+ if (result == null) {
+ Log.warning("Malformed string array property [fqn=" + name +
+ ", value=" + val + "].");
+ return defval;
+ }
+
+ return result;
+ }
+
+ protected String resolveProperty (String name)
+ {
+ int didx = name.indexOf(".");
+ if (didx == -1) {
+ Log.warning("Invalid fully qualified property name " +
+ "[name=" + name + "].");
+ return null;
+ }
+
+ String id = name.substring(0, didx);
+ String key = name.substring(didx+1);
+
+ Properties props = (Properties)_props.get(id);
+ if (props == null) {
+ Log.warning("No property file bound to top-level name " +
+ "[name=" + id + ", key=" + key + "].");
+ return null;
+ }
+
+ return props.getProperty(key);
+ }
+
+ public static void main (String[] args)
+ {
+ Config config = new Config();
+ try {
+ config.bindProperties("test", "com/samskivert/util/test");
+
+ System.out.println("test.prop1: " +
+ config.getValue("test.prop1", 1));
+ System.out.println("test.prop2: " +
+ config.getValue("test.prop2", "two"));
+
+ int[] ival = new int[] { 1, 2, 3 };
+ ival = config.getValue("test.prop3", ival);
+ System.out.println("test.prop3: " + StringUtil.toString(ival));
+
+ String[] sval = new String[] { "one", "two", "three" };
+ sval = config.getValue("test.prop4", sval);
+ System.out.println("test.prop4: " + StringUtil.toString(sval));
+
+ System.out.println("test.prop5: " +
+ config.getValue("test.prop5", "undefined"));
+
+ } catch (IOException ioe) {
+ ioe.printStackTrace(System.err);
+ }
+ }
+
+ protected Hashtable _props = new Hashtable();
+
+ protected static final String PROPS_SUFFIX = ".properties";
+}
diff --git a/projects/samskivert/tests/rsrc/util/test.properties b/projects/samskivert/tests/rsrc/util/test.properties
new file mode 100644
index 00000000..0252ea9c
--- /dev/null
+++ b/projects/samskivert/tests/rsrc/util/test.properties
@@ -0,0 +1,10 @@
+#
+# $Id: test.properties,v 1.1 2001/07/12 02:44:34 mdb Exp $
+#
+# A test properties file
+
+prop1 = 25
+prop2 = twenty five
+prop3 = 9, 8, 7, 6
+prop4 = one, two, three,, and a half, four
+# prop5 = not defined