diff --git a/src/java/org/apache/commons/collections/ExtendedProperties.java b/src/java/org/apache/commons/collections/ExtendedProperties.java
new file mode 100644
index 000000000..7bddcfdc6
--- /dev/null
+++ b/src/java/org/apache/commons/collections/ExtendedProperties.java
@@ -0,0 +1,1823 @@
+package org.apache.commons.collections;
+
+/*
+ * Copyright (c) 2001 The Java Apache Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Java Apache
+ * Project for use in the Apache JServ servlet engine project
+ *
The Extended Properties syntax is explained here: + * + *
key = value
+ * Here is an example of a valid extended properties file: + * + *
+ * # lines starting with # are comments + * + * # This is the simplest property + * key = value + * + * # A long property may be separated on multiple lines + * longvalue = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ + * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + * + * # This is a property with many tokens + * tokens_on_a_line = first token, second token + * + * # This sequence generates exactly the same result + * tokens_on_multiple_lines = first token + * tokens_on_multiple_lines = second token + * + * # commas may be escaped in tokens + * commas.excaped = Hi\, what'up? + *+ * + *
NOTE: this class has not been written for
+ * performance nor low memory usage. In fact, it's way slower than it
+ * could be and generates too much memory garbage. But since
+ * performance is not an issue during intialization (and there is not
+ * much time to improve it), I wrote it this way. If you don't like
+ * it, go ahead and tune it up!
+ *
+ *
+ * @author Stefano Mazzocchi
+ * @author Jon S. Stevens
+ * @author Dave Bryson
+ * @author Jason van Zyl
+ * @author Geir Magnusson Jr.
+ * @author Leon Messerschmidt
+ * @version $Id: ExtendedProperties.java,v 1.1 2001/04/16 05:01:03 geirm Exp $
+ */
+public class ExtendedProperties extends Hashtable
+{
+ /**
+ * Default configurations repository.
+ */
+ private ExtendedProperties defaults;
+
+ /**
+ * The file connected to this repository (holding comments and
+ * such).
+ *
+ * @serial
+ */
+ protected String file;
+
+ /**
+ * Base path of the configuration file used to create
+ * this Configuration object.
+ */
+ protected String basePath;
+
+ /**
+ * File separator.
+ */
+ protected String fileSeparator = System.getProperty("file.separator");
+
+ /**
+ * Has this configuration been intialized.
+ */
+ protected boolean isInitialized = false;
+
+ /**
+ * This is the name of the property that can point to other
+ * properties file for including other properties files.
+ */
+ protected static String include = "include";
+
+ /**
+ * These are the keys in the order they listed
+ * in the configuration file. This is useful when
+ * you wish to perform operations with configuration
+ * information in a particular order.
+ */
+ protected ArrayList keysAsListed = new ArrayList();
+
+ /**
+ * This class is used to read properties lines. These lines do
+ * not terminate with new-line chars but rather when there is no
+ * backslash sign a the end of the line. This is used to
+ * concatenate multiple lines for readability.
+ */
+ class PropertiesReader extends LineNumberReader
+ {
+ /**
+ * Constructor.
+ *
+ * @param reader A Reader.
+ */
+ public PropertiesReader(Reader reader)
+ {
+ super(reader);
+ }
+
+ /**
+ * Read a property.
+ *
+ * @return A String.
+ * @exception IOException.
+ */
+ public String readProperty() throws IOException
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ try
+ {
+ while (true)
+ {
+ String line = readLine().trim();
+ if ((line.length() != 0) && (line.charAt(0) != '#'))
+ {
+ if (line.endsWith("\\"))
+ {
+ line = line.substring(0, line.length() - 1);
+ buffer.append(line);
+ }
+ else
+ {
+ buffer.append(line);
+ break;
+ }
+ }
+ }
+ }
+ catch (NullPointerException e)
+ {
+ return null;
+ }
+
+ return buffer.toString();
+ }
+ }
+
+ /**
+ * This class divides into tokens a property value. Token
+ * separator is "," but commas into the property value are escaped
+ * using the backslash in front.
+ */
+ class PropertiesTokenizer extends StringTokenizer
+ {
+ /**
+ * Constructor.
+ *
+ * @param string A String.
+ */
+ public PropertiesTokenizer(String string)
+ {
+ super(string, ",");
+ }
+
+ /**
+ * Check whether the object has more tokens.
+ *
+ * @return True if the object has more tokens.
+ */
+ public boolean hasMoreTokens()
+ {
+ return super.hasMoreTokens();
+ }
+
+ /**
+ * Get next token.
+ *
+ * @return A String.
+ */
+ public String nextToken()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ while (hasMoreTokens())
+ {
+ String token = super.nextToken();
+ if (token.endsWith("\\"))
+ {
+ buffer.append(token.substring(0, token.length() - 1));
+ buffer.append(",");
+ }
+ else
+ {
+ buffer.append(token);
+ break;
+ }
+ }
+
+ return buffer.toString().trim();
+ }
+ }
+
+ /**
+ * Creates an empty extended properties object.
+ */
+ public ExtendedProperties ()
+ {
+ super();
+ }
+
+ /**
+ * Creates and loads the extended properties from the specified
+ * file.
+ *
+ * @param file A String.
+ * @exception IOException.
+ */
+ public ExtendedProperties(String file)
+ throws IOException
+ {
+ this(file,null);
+ }
+
+ /**
+ * Creates and loads the extended properties from the specified
+ * file.
+ *
+ * @param file A String.
+ * @exception IOException.
+ */
+ public ExtendedProperties (String file, String defaultFile)
+ throws IOException
+ {
+ this.file = file;
+
+ basePath = new File(file).getAbsolutePath();
+ basePath = basePath.substring(0, basePath.lastIndexOf(fileSeparator) + 1);
+
+ this.load(new FileInputStream(file));
+
+ if (defaultFile != null)
+ {
+ defaults = new ExtendedProperties(defaultFile);
+ }
+ }
+
+ /**
+ * Private initializer method that sets up the generic
+ * resources.
+ *
+ * @exception IOException, if there was an I/O problem.
+ */
+ private void init( ExtendedProperties exp ) throws IOException
+ {
+ isInitialized = true;
+ }
+
+ /**
+ * Indicate to client code whether property
+ * resources have been initialized or not.
+ */
+ public boolean isInitialized()
+ {
+ return isInitialized;
+ }
+
+ /**
+ * Gets the property value for including other properties files.
+ * By default it is "include".
+ *
+ * @return A String.
+ */
+ public String getInclude()
+ {
+ return this.include;
+ }
+
+ /**
+ * Sets the property value for including other properties files.
+ * By default it is "include".
+ *
+ * @param inc A String.
+ */
+ public void setInclude(String inc)
+ {
+ this.include = inc;
+ }
+
+ /**
+ * Load the properties from the given input stream.
+ *
+ * @param input An InputStream.
+ * @exception IOException.
+ */
+ public synchronized void load(InputStream input)
+ throws IOException
+ {
+ PropertiesReader reader =
+ new PropertiesReader(new InputStreamReader(input));
+
+ try
+ {
+ while (true)
+ {
+ String line = reader.readProperty();
+ int equalSign = line.indexOf('=');
+
+ if (equalSign > 0)
+ {
+ String key = line.substring(0, equalSign).trim();
+ String value = line.substring(equalSign + 1).trim();
+
+ /*
+ * Configure produces lines like this ... just
+ * ignore them.
+ */
+ if ("".equals(value))
+ continue;
+
+ if (getInclude() != null &&
+ key.equalsIgnoreCase(getInclude()))
+ {
+ /*
+ * Recursively load properties files.
+ */
+ File file = null;
+
+ if (value.startsWith(fileSeparator))
+ {
+ /*
+ * We have an absolute path so we'll
+ * use this.
+ */
+ file = new File(value);
+ }
+ else
+ {
+ /*
+ * We have a relative path, and we have
+ * two possible forms here. If we have the
+ * "./" form then just strip that off first
+ * before continuing.
+ */
+ if (value.startsWith("." + fileSeparator))
+ {
+ value = value.substring(2);
+ }
+
+ file = new File(basePath + value);
+ }
+
+ if (file != null && file.exists() && file.canRead())
+ {
+ load ( new FileInputStream(file));
+ }
+ }
+ else
+ {
+ addProperty(key,value);
+ //setProperty(key,value);
+ }
+ }
+ }
+ }
+ catch (NullPointerException e)
+ {
+ /*
+ * Should happen only when EOF is reached.
+ */
+ return;
+ }
+ }
+
+ /**
+ * Gets a property from the configuration.
+ *
+ * @param key property to retrieve
+ * @return value as object. Will return user value if exists,
+ * if not then default value if exists, otherwise null
+ */
+ public Object getProperty( String key)
+ {
+ /*
+ * first, try to get from the 'user value' store
+ */
+ Object o = this.get(key);
+
+ if ( o == null)
+ {
+ /*
+ * if there isn't a value there, get it from the
+ * defaults if we have them
+ */
+
+ if (defaults != null)
+ {
+ o = defaults.get(key);
+ }
+ }
+
+ return o;
+ }
+
+ /**
+ * Add a property to the configuration. If it already
+ * exists then the value stated here will be added
+ * to the configuration entry. For example, if
+ *
+ * resource.loader = file
+ *
+ * is already present in the configuration and you
+ *
+ * addProperty("resource.loader", "classpath")
+ *
+ * Then you will end up with a Vector like the
+ * following:
+ *
+ * ["file", "classpath"]
+ *
+ * @param String key
+ * @param String value
+ */
+ //public void setProperty(String key, Object token)
+ public void addProperty(String key, Object token)
+ {
+ Object o = this.get(key);
+
+ /*
+ * $$$ GMJ
+ * FIXME : post 1.0 release, we need to not assume
+ * that a scalar is a String - it can be an Object
+ * so we should make a little vector-like class
+ * say, Foo that wraps (not extends Vector),
+ * so we can do things like
+ * if ( !( o instanceof Foo) )
+ * so we know it's our 'vector' container
+ *
+ * This applies throughout
+ */
+
+ if (o instanceof String)
+ {
+ Vector v = new Vector(2);
+ v.addElement(o);
+ v.addElement(token);
+ put(key, v);
+ }
+ else if (o instanceof Vector)
+ {
+ ((Vector) o).addElement(token);
+ }
+ else
+ {
+ /*
+ * This is the first time that we have seen
+ * request to place an object in the
+ * configuration with the key 'key'. So
+ * we just want to place it directly into
+ * the configuration ... but we are going to
+ * make a special exception for String objects
+ * that contain "," characters. We will take
+ * CSV lists and turn the list into a vector of
+ * Strings before placing it in the configuration.
+ * This is a concession for Properties and the
+ * like that cannot parse multiple same key
+ * values.
+ */
+ if (token instanceof String && ((String)token).indexOf(",") > 0)
+ {
+ PropertiesTokenizer tokenizer =
+ new PropertiesTokenizer((String)token);
+
+ while (tokenizer.hasMoreTokens())
+ {
+ String value = tokenizer.nextToken();
+
+ /*
+ * we know this is a string, so make sure it
+ * just goes in rather than risking vectorization
+ * if it contains an escaped comma
+ */
+ addStringProperty(key,value);
+ }
+ }
+ else
+ {
+ /*
+ * We want to keep track of the order the keys
+ * are parsed, or dynamically entered into
+ * the configuration. So when we see a key
+ * for the first time we will place it in
+ * an ArrayList so that if a client class needs
+ * to perform operations with configuration
+ * in a definite order it will be possible.
+ */
+
+ /*
+ * safety check
+ */
+
+ if( !containsKey( key ) )
+ {
+ keysAsListed.add(key);
+ }
+
+ /*
+ * and the value
+ */
+ put(key, token);
+ }
+ }
+ }
+
+
+ /**
+ * Sets a string property w/o checking for commas - used
+ * internally when a property has been broken up into
+ * strings that could contain escaped commas to prevent
+ * the inadvertant vectorization.
+ *
+ * Thanks to Leon Messerschmidt for this one.
+ *
+ */
+ private void addStringProperty(String key, String token)
+ {
+ Object o = this.get(key);
+
+ /*
+ * $$$ GMJ
+ * FIXME : post 1.0 release, we need to not assume
+ * that a scalar is a String - it can be an Object
+ * so we should make a little vector-like class
+ * say, Foo that wraps (not extends Vector),
+ * so we can do things like
+ * if ( !( o instanceof Foo) )
+ * so we know it's our 'vector' container
+ *
+ * This applies throughout
+ */
+
+ /*
+ * do the usual thing - if we have a value and
+ * it's scalar, make a vector, otherwise add
+ * to the vector
+ */
+
+ if (o instanceof String)
+ {
+ Vector v = new Vector(2);
+ v.addElement(o);
+ v.addElement(token);
+ put(key, v);
+ }
+ else if (o instanceof Vector)
+ {
+ ((Vector) o).addElement(token);
+ }
+ else
+ {
+ if( !containsKey( key ) )
+ {
+ keysAsListed.add(key);
+ }
+
+ put( key, token);
+ }
+ }
+
+ /**
+ * Set a property, this will replace any previously
+ * set values. Set values is implicitly a call
+ * to clearProperty(key), addProperty(key,value).
+ *
+ * @param String key
+ * @param String value
+ */
+ public void setProperty(String key, Object value)
+ {
+ clearProperty(key);
+ addProperty(key,value);
+ }
+
+ /**
+ * Save the properties to the given outputstream.
+ *
+ * @param output An OutputStream.
+ * @param header A String.
+ * @exception IOException.
+ */
+ public synchronized void save(OutputStream output,
+ String Header)
+ throws IOException
+ {
+ if(output != null)
+ {
+ PrintWriter theWrtr = new PrintWriter(output);
+ if(Header != null)
+ {
+ theWrtr.println(Header);
+ }
+ Enumeration theKeys = keys();
+ while(theKeys.hasMoreElements())
+ {
+ String key = (String) theKeys.nextElement();
+ Object value = get((Object) key);
+ if(value != null)
+ {
+ if(value instanceof String)
+ {
+ StringBuffer currentOutput = new StringBuffer();
+ currentOutput.append(key);
+ currentOutput.append("=");
+ currentOutput.append((String) value);
+ theWrtr.println(currentOutput.toString());
+ }
+ else if(value instanceof Vector)
+ {
+ Vector values = (Vector) value;
+ Enumeration valuesEnum = values.elements();
+ while(valuesEnum.hasMoreElements())
+ {
+ String currentElement =
+ (String) valuesEnum.nextElement();
+ StringBuffer currentOutput = new StringBuffer();
+ currentOutput.append(key);
+ currentOutput.append("=");
+ currentOutput.append(currentElement);
+ theWrtr.println(currentOutput.toString());
+ }
+ }
+ }
+ theWrtr.println();
+ theWrtr.flush();
+ }
+ }
+ }
+
+ /**
+ * Combines an existing Hashtable with this Hashtable.
+ *
+ * Warning: It will overwrite previous entries without warning.
+ *
+ * @param Configuration
+ */
+ public void combine( ExtendedProperties c)
+ {
+ for (Iterator i = c.getKeys() ; i.hasNext() ;)
+ {
+ String key = (String) i.next();
+ //clearProperty(key);
+ setProperty( key, c.get(key) );
+ }
+ }
+
+ /**
+ * Clear a property in the configuration.
+ *
+ * @param String key to remove along with corresponding value.
+ */
+ public void clearProperty(String key)
+ {
+ if (containsKey(key))
+ {
+ /*
+ * we also need to rebuild the keysAsListed or else
+ * things get *very* confusing
+ */
+
+ for(int i = 0; i < keysAsListed.size(); i++)
+ {
+ if ( ( (String) keysAsListed.get(i)).equals( key ) )
+ {
+ keysAsListed.remove(i);
+ break;
+ }
+ }
+
+ remove(key);
+ }
+ }
+
+ /**
+ * Get the list of the keys contained in the configuration
+ * repository.
+ *
+ * @return An Iterator.
+ */
+ public Iterator getKeys()
+ {
+ return keysAsListed.iterator();
+ }
+
+ /**
+ * Get the list of the keys contained in the configuration
+ * repository that match the specified prefix.
+ *
+ * @param prefix The prefix to test against.
+ * @return An Iterator of keys that match the prefix.
+ */
+ public Iterator getKeys(String prefix)
+ {
+ Iterator keys = getKeys();
+ ArrayList matchingKeys = new ArrayList();
+
+ while( keys.hasNext() )
+ {
+ Object key = keys.next();
+
+ if( key instanceof String && ((String) key).startsWith(prefix) )
+ {
+ matchingKeys.add(key);
+ }
+ }
+ return matchingKeys.iterator();
+ }
+
+ /**
+ * Create a Configurations object that is a subset
+ * of this one. Take into account duplicate keys
+ * by using the setProperty() in Configuration.
+ *
+ * @param String prefix
+ */
+ public ExtendedProperties subset(String prefix)
+ {
+ ExtendedProperties c = new ExtendedProperties();
+ Iterator keys = getKeys();
+ boolean validSubset = false;
+
+ while( keys.hasNext() )
+ {
+ Object key = keys.next();
+
+ if( key instanceof String && ((String) key).startsWith(prefix) )
+ {
+ if (!validSubset)
+ {
+ validSubset = true;
+ }
+
+ String newKey = null;
+
+ /*
+ * Check to make sure that c.subset(prefix) doesn't
+ * blow up when there is only a single property
+ * with the key prefix. This is not a useful
+ * subset but it is a valid subset.
+ */
+ if ( ((String)key).length() == prefix.length())
+ {
+ newKey = prefix;
+ }
+ else
+ {
+ newKey = ((String)key).substring(prefix.length() + 1);
+ }
+
+ /*
+ * Make sure to use the setProperty() method and not
+ * just put(). setProperty() takes care of catching
+ * all the keys in the order they appear in a
+ * properties files or the order they are set
+ * dynamically.
+ */
+
+ c.setProperty(newKey, get(key));
+ }
+ }
+
+ if (validSubset)
+ {
+ return c;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Display the configuration for debugging
+ * purposes.
+ */
+ public void display()
+ {
+ Iterator i = getKeys();
+
+ while (i.hasNext())
+ {
+ String key = (String) i.next();
+ Object value = get(key);
+ System.out.println(key + " => " + value);
+ }
+ }
+
+ /**
+ * Get a string associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated string.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a String.
+ */
+ public String getString(String key)
+ {
+ return getString(key, null);
+ }
+
+ /**
+ * Get a string associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated string if key is found,
+ * default value otherwise.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a String.
+ */
+ public String getString(String key,
+ String defaultValue)
+ {
+ Object value = get(key);
+
+ if (value instanceof String)
+ {
+ return (String) value;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getString(key, defaultValue);
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a String object");
+ }
+ }
+
+ /**
+ * Get a list of properties associated with the given
+ * configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated properties if key is found.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a String/Vector.
+ * @exception IllegalArgumentException if one of the tokens is
+ * malformed (does not contain an equals sign).
+ */
+ public Properties getProperties(String key)
+ {
+ return getProperties(key, new Properties());
+ }
+
+ /**
+ * Get a list of properties associated with the given
+ * configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated properties if key is found.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a String/Vector.
+ * @exception IllegalArgumentException if one of the tokens is
+ * malformed (does not contain an equals sign).
+ */
+ public Properties getProperties(String key,
+ Properties defaults)
+ {
+ /*
+ * Grab an array of the tokens for this key.
+ */
+ String[] tokens = getStringArray(key);
+
+ /*
+ * Each token is of the form 'key=value'.
+ */
+ Properties props = new Properties(defaults);
+ for (int i = 0; i < tokens.length; i++)
+ {
+ String token = tokens[i];
+ int equalSign = token.indexOf('=');
+ if (equalSign > 0)
+ {
+ String pkey = token.substring(0, equalSign).trim();
+ String pvalue = token.substring(equalSign + 1).trim();
+ props.put(pkey, pvalue);
+ }
+ else
+ {
+ throw new IllegalArgumentException("'" +
+ token +
+ "' does not contain " +
+ "an equals sign");
+ }
+ }
+ return props;
+ }
+
+ /**
+ * Get an array of strings associated with the given configuration
+ * key.
+ *
+ * @param key The configuration key.
+ * @return The associated string array if key is found.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a String/Vector.
+ */
+ public String[] getStringArray(String key)
+ {
+ Object value = get(key);
+
+ // What's your vector, Victor?
+ Vector vector;
+ if (value instanceof String)
+ {
+ vector = new Vector(1);
+ vector.addElement(value);
+ }
+ else if (value instanceof Vector)
+ {
+ vector = (Vector)value;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getStringArray(key);
+ }
+ else
+ {
+ return new String[0];
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a String/Vector object");
+ }
+
+ String[] tokens = new String[vector.size()];
+ for (int i = 0; i < tokens.length; i++)
+ tokens[i] = (String)vector.elementAt(i);
+
+ return tokens;
+ }
+
+ /**
+ * Get a Vector of strings associated with the given configuration
+ * key.
+ *
+ * @param key The configuration key.
+ * @return The associated Vector.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Vector.
+ */
+ public Vector getVector(String key)
+ {
+ return getVector(key, null);
+ }
+
+ /**
+ * Get a Vector of strings associated with the given configuration
+ * key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated Vector.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Vector.
+ */
+ public Vector getVector(String key,
+ Vector defaultValue)
+ {
+ Object value = get(key);
+
+ if (value instanceof Vector)
+ {
+ return (Vector) value;
+ }
+ else if (value instanceof String)
+ {
+ Vector v = new Vector(1);
+ v.addElement((String) value);
+ put(key, v);
+ return v;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getVector(key, defaultValue);
+ }
+ else
+ {
+ return ((defaultValue == null) ?
+ new Vector() : defaultValue);
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a Vector object");
+ }
+ }
+
+ /**
+ * Get a boolean associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated boolean.
+ * @exception NoSuchElementException is thrown if the key doesn't
+ * map to an existing object.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Boolean.
+ */
+ public boolean getBoolean(String key)
+ {
+ Boolean b = getBoolean(key, (Boolean) null);
+ if (b != null)
+ {
+ return b.booleanValue();
+ }
+ else
+ {
+ throw new NoSuchElementException(
+ key + "doesn't map to an existing object");
+ }
+ }
+
+ /**
+ * Get a boolean associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated boolean.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Boolean.
+ */
+ public boolean getBoolean(String key, boolean defaultValue)
+ {
+ return getBoolean(key, new Boolean(defaultValue)).booleanValue();
+ }
+
+ /**
+ * Get a boolean associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated boolean if key is found and has valid
+ * format, default value otherwise.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Boolean.
+ */
+ public Boolean getBoolean(String key, Boolean defaultValue)
+ {
+
+ Object value = get(key);
+
+ if (value instanceof Boolean)
+ {
+ return (Boolean) value;
+ }
+ else if (value instanceof String)
+ {
+ String s = testBoolean((String)value);
+ Boolean b = new Boolean(s);
+ put(key, b);
+ return b;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getBoolean(key, defaultValue);
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a Boolean object");
+ }
+ }
+
+ /**
+ * Test whether the string represent by value maps to a boolean
+ * value or not. We will allow true
, on
,
+ * and yes
for a true
boolean value, and
+ * false
, off
, and no
for
+ * false
boolean values. Case of value to test for
+ * boolean status is ignored.
+ *
+ * @param String The value to test for boolean state.
+ * @return true
or false
if the supplied
+ * text maps to a boolean value, or null
otherwise.
+ */
+ public String testBoolean(String value)
+ {
+ String s = ((String)value).toLowerCase();
+
+ if (s.equals("true") || s.equals("on") || s.equals("yes"))
+ {
+ return "true";
+ }
+ else if (s.equals("false") || s.equals("off") || s.equals("no"))
+ {
+ return "false";
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Get a byte associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated byte.
+ * @exception NoSuchElementException is thrown if the key doesn't
+ * map to an existing object.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Byte.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public byte getByte(String key)
+ {
+ Byte b = getByte(key, null);
+ if (b != null)
+ {
+ return b.byteValue();
+ }
+ else
+ {
+ throw new NoSuchElementException(
+ key + " doesn't map to an existing object");
+ }
+ }
+
+ /**
+ * Get a byte associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated byte.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Byte.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public byte getByte(String key,
+ byte defaultValue)
+ {
+ return getByte(key, new Byte(defaultValue)).byteValue();
+ }
+
+ /**
+ * Get a byte associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated byte if key is found and has valid
+ * format, default value otherwise.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Byte.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public Byte getByte(String key,
+ Byte defaultValue)
+ {
+ Object value = get(key);
+
+ if (value instanceof Byte)
+ {
+ return (Byte) value;
+ }
+ else if (value instanceof String)
+ {
+ Byte b = new Byte((String) value);
+ put(key, b);
+ return b;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getByte(key, defaultValue);
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a Byte object");
+ }
+ }
+
+ /**
+ * Get a short associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated short.
+ * @exception NoSuchElementException is thrown if the key doesn't
+ * map to an existing object.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Short.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public short getShort(String key)
+ {
+ Short s = getShort(key, null);
+ if (s != null)
+ {
+ return s.shortValue();
+ }
+ else
+ {
+ throw new NoSuchElementException(
+ key + " doesn't map to an existing object");
+ }
+ }
+
+ /**
+ * Get a short associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated short.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Short.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public short getShort(String key,
+ short defaultValue)
+ {
+ return getShort(key, new Short(defaultValue)).shortValue();
+ }
+
+ /**
+ * Get a short associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated short if key is found and has valid
+ * format, default value otherwise.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Short.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public Short getShort(String key,
+ Short defaultValue)
+ {
+ Object value = get(key);
+
+ if (value instanceof Short)
+ {
+ return (Short) value;
+ }
+ else if (value instanceof String)
+ {
+ Short s = new Short((String) value);
+ put(key, s);
+ return s;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getShort(key, defaultValue);
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a Short object");
+ }
+ }
+
+ /**
+ * The purpose of this method is to get the configuration resource
+ * with the given name as an integer.
+ *
+ * @param name The resource name.
+ * @return The value of the resource as an integer.
+ */
+ public int getInt(String name)
+ {
+ return getInteger(name);
+ }
+
+ /**
+ * The purpose of this method is to get the configuration resource
+ * with the given name as an integer, or a default value.
+ *
+ * @param name The resource name
+ * @param def The default value of the resource.
+ * @return The value of the resource as an integer.
+ */
+ public int getInt(String name,
+ int def)
+ {
+ return getInteger(name, def);
+ }
+
+ /**
+ * Get a int associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated int.
+ * @exception NoSuchElementException is thrown if the key doesn't
+ * map to an existing object.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Integer.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public int getInteger(String key)
+ {
+ Integer i = getInteger(key, null);
+ if (i != null)
+ {
+ return i.intValue();
+ }
+ else
+ {
+ throw new NoSuchElementException(
+ key + " doesn't map to an existing object");
+ }
+ }
+
+ /**
+ * Get a int associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated int.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Integer.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public int getInteger(String key,
+ int defaultValue)
+ {
+ return getInteger(key, new Integer(defaultValue)).intValue();
+ }
+
+ /**
+ * Get a int associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated int if key is found and has valid
+ * format, default value otherwise.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Integer.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public Integer getInteger(String key,
+ Integer defaultValue)
+ {
+ Object value = get(key);
+
+ if (value instanceof Integer)
+ {
+ return (Integer) value;
+ }
+ else if (value instanceof String)
+ {
+ Integer i = new Integer((String) value);
+ put(key, i);
+ return i;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getInteger(key, defaultValue);
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a Integer object");
+ }
+ }
+
+ /**
+ * Get a long associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated long.
+ * @exception NoSuchElementException is thrown if the key doesn't
+ * map to an existing object.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Long.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public long getLong(String key)
+ {
+ Long l = getLong(key, null);
+ if (l != null)
+ {
+ return l.longValue();
+ }
+ else
+ {
+ throw new NoSuchElementException(
+ key + " doesn't map to an existing object");
+ }
+ }
+
+ /**
+ * Get a long associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated long.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Long.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public long getLong(String key,
+ long defaultValue)
+ {
+ return getLong(key, new Long(defaultValue)).longValue();
+ }
+
+ /**
+ * Get a long associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated long if key is found and has valid
+ * format, default value otherwise.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Long.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public Long getLong(String key,
+ Long defaultValue)
+ {
+ Object value = get(key);
+
+ if (value instanceof Long)
+ {
+ return (Long) value;
+ }
+ else if (value instanceof String)
+ {
+ Long l = new Long((String) value);
+ put(key, l);
+ return l;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getLong(key, defaultValue);
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a Long object");
+ }
+ }
+
+ /**
+ * Get a float associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated float.
+ * @exception NoSuchElementException is thrown if the key doesn't
+ * map to an existing object.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Float.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public float getFloat(String key)
+ {
+ Float f = getFloat(key, null);
+ if (f != null)
+ {
+ return f.floatValue();
+ }
+ else
+ {
+ throw new NoSuchElementException(
+ key + " doesn't map to an existing object");
+ }
+ }
+
+ /**
+ * Get a float associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated float.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Float.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public float getFloat(String key,
+ float defaultValue)
+ {
+ return getFloat(key, new Float(defaultValue)).floatValue();
+ }
+
+ /**
+ * Get a float associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated float if key is found and has valid
+ * format, default value otherwise.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Float.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public Float getFloat(String key,
+ Float defaultValue)
+ {
+ Object value = get(key);
+
+ if (value instanceof Float)
+ {
+ return (Float) value;
+ }
+ else if (value instanceof String)
+ {
+ Float f = new Float((String) value);
+ put(key, f);
+ return f;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getFloat(key, defaultValue);
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a Float object");
+ }
+ }
+
+ /**
+ * Get a double associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @return The associated double.
+ * @exception NoSuchElementException is thrown if the key doesn't
+ * map to an existing object.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Double.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public double getDouble(String key)
+ {
+ Double d = getDouble(key, null);
+ if (d != null)
+ {
+ return d.doubleValue();
+ }
+ else
+ {
+ throw new NoSuchElementException(
+ key + " doesn't map to an existing object");
+ }
+ }
+
+ /**
+ * Get a double associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated double.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Double.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public double getDouble(String key,
+ double defaultValue)
+ {
+ return getDouble(key, new Double(defaultValue)).doubleValue();
+ }
+
+ /**
+ * Get a double associated with the given configuration key.
+ *
+ * @param key The configuration key.
+ * @param defaultValue The default value.
+ * @return The associated double if key is found and has valid
+ * format, default value otherwise.
+ * @exception ClassCastException is thrown if the key maps to an
+ * object that is not a Double.
+ * @exception NumberFormatException is thrown if the value mapped
+ * by the key has not a valid number format.
+ */
+ public Double getDouble(String key,
+ Double defaultValue)
+ {
+ Object value = get(key);
+
+ if (value instanceof Double)
+ {
+ return (Double) value;
+ }
+ else if (value instanceof String)
+ {
+ Double d = new Double((String) value);
+ put(key, d);
+ return d;
+ }
+ else if (value == null)
+ {
+ if (defaults != null)
+ {
+ return defaults.getDouble(key, defaultValue);
+ }
+ else
+ {
+ return defaultValue;
+ }
+ }
+ else
+ {
+ throw new ClassCastException(
+ key + " doesn't map to a Double object");
+ }
+ }
+
+ /**
+ * Convert a standard properties class into a configuration
+ * class.
+ *
+ * @param Properties properties object to convert into
+ * a Configuration object.
+ *
+ * @return Configuration configuration created from the
+ * properties object.
+ */
+ public static ExtendedProperties convertProperties(Properties p)
+ {
+ ExtendedProperties c = new ExtendedProperties();
+
+ for (Enumeration e = p.keys(); e.hasMoreElements() ; )
+ {
+ String s = (String) e.nextElement();
+ c.setProperty(s, p.getProperty(s));
+ }
+
+ return c;
+ }
+}