/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.commons.collections;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

This class extends normal Java properties by adding the possibility to use the same key many times concatenating the value strings instead of overwriting them.

Please consider using the PropertiesConfiguration class in Commons-Configuration as soon as it is released.

The Extended Properties syntax is explained here:

  • Each property has the syntax key = value
  • The key may use any character but the equal sign '='.
  • value may be separated on different lines if a backslash is placed at the end of the line that continues below.
  • If value is a list of strings, each token is separated by a comma ','.
  • Commas in each token are escaped placing a backslash right before the comma.
  • Backslashes are escaped by using two consecutive backslashes i.e. \\
  • If a key is used more than once, the values are appended as if they were on the same line separated with commas.
  • Blank lines and lines starting with character '#' are skipped.
  • If a property is named "include" (or whatever is defined by setInclude() and getInclude() and the value of that property is the full path to a file on disk, that file will be included into the ConfigurationsRepository. You can also pull in files relative to the parent configuration file. So if you have something like the following: include = additional.properties Then "additional.properties" is expected to be in the same directory as the parent configuration file. Duplicate name values will be replaced, so be careful.

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.escaped = 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, Jon S. Stevens, Dave Bryson, Jason van Zyl, Geir Magnusson Jr., Leon Messerschmidt, Kent Johnson, Daniel Rall, Ilkka Priha, Janek Bogucki, Mohan Kishore, Stephen Colebourne, Shinobu Kawai, Henning P. Schmiedehausen
Since:Commons Collections 1.0
Version:$Revision: 1713233 $ $Date: 2015-11-08 16:05:59 +0100 (Sun, 08 Nov 2015) $
/** * This class extends normal Java properties by adding the possibility * to use the same key many times concatenating the value strings * instead of overwriting them. * <p> * <b>Please consider using the <code>PropertiesConfiguration</code> class in * Commons-Configuration as soon as it is released.</b> * <p> * The Extended Properties syntax is explained here: * * <ul> * <li> * Each property has the syntax <code>key = value</code> * </li> * <li> * The <i>key</i> may use any character but the equal sign '='. * </li> * <li> * <i>value</i> may be separated on different lines if a backslash * is placed at the end of the line that continues below. * </li> * <li> * If <i>value</i> is a list of strings, each token is separated * by a comma ','. * </li> * <li> * Commas in each token are escaped placing a backslash right before * the comma. * </li> * <li> * Backslashes are escaped by using two consecutive backslashes i.e. \\ * </li> * <li> * If a <i>key</i> is used more than once, the values are appended * as if they were on the same line separated with commas. * </li> * <li> * Blank lines and lines starting with character '#' are skipped. * </li> * <li> * If a property is named "include" (or whatever is defined by * setInclude() and getInclude() and the value of that property is * the full path to a file on disk, that file will be included into * the ConfigurationsRepository. You can also pull in files relative * to the parent configuration file. So if you have something * like the following: * * include = additional.properties * * Then "additional.properties" is expected to be in the same * directory as the parent configuration file. * * Duplicate name values will be replaced, so be careful. * * </li> * </ul> * * <p>Here is an example of a valid extended properties file: * * <p><pre> * # 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.escaped = Hi\, what'up? * </pre> * * <p><b>NOTE</b>: this class has <b>not</b> 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! * * @since Commons Collections 1.0 * @version $Revision: 1713233 $ $Date: 2015-11-08 16:05:59 +0100 (Sun, 08 Nov 2015) $ * * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a> * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a> * @author <a href="mailto:daveb@miceda-data">Dave Bryson</a> * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a> * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a> * @author <a href="mailto:kjohnson@transparent.com">Kent Johnson</a> * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> * @author <a href="mailto:ipriha@surfeu.fi">Ilkka Priha</a> * @author Janek Bogucki * @author Mohan Kishore * @author Stephen Colebourne * @author Shinobu Kawai * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> */
public class ExtendedProperties extends Hashtable {
Default configurations repository.
/** * Default configurations repository. */
private ExtendedProperties defaults;
The file connected to this repository (holding comments and such).
@serial
/** * The file connected to this repository (holding comments and * such). * * @serial */
protected String file;
Base path of the configuration file used to create this ExtendedProperties object.
/** * Base path of the configuration file used to create * this ExtendedProperties object. */
protected String basePath;
File separator.
/** * File separator. */
protected String fileSeparator; { try { fileSeparator = (String) AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { return System.getProperty("file.separator"); } }); } catch (SecurityException ex) { fileSeparator = File.separator; } }
Has this configuration been initialized.
/** * Has this configuration been initialized. */
protected boolean isInitialized = false;
This is the name of the property that can point to other properties file for including other properties files.
/** * 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.
/** * 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(); protected final static String START_TOKEN="${"; protected final static String END_TOKEN="}";
Interpolate key names to handle ${key} stuff
Params:
  • base – string to interpolate
Returns:returns the key name with the ${key} substituted
/** * Interpolate key names to handle ${key} stuff * * @param base string to interpolate * @return returns the key name with the ${key} substituted */
protected String interpolate(String base) { // COPIED from [configuration] 2003-12-29 return (interpolateHelper(base, null)); }
Recursive handler for multiple levels of interpolation. When called the first time, priorVariables should be null.
Params:
  • base – string with the ${key} variables
  • priorVariables – serves two purposes: to allow checking for loops, and creating a meaningful exception message should a loop occur. It's 0'th element will be set to the value of base from the first call. All subsequent interpolated variables are added afterward.
Returns:the string with the interpolation taken care of
/** * Recursive handler for multiple levels of interpolation. * * When called the first time, priorVariables should be null. * * @param base string with the ${key} variables * @param priorVariables serves two purposes: to allow checking for * loops, and creating a meaningful exception message should a loop * occur. It's 0'th element will be set to the value of base from * the first call. All subsequent interpolated variables are added * afterward. * * @return the string with the interpolation taken care of */
protected String interpolateHelper(String base, List priorVariables) { // COPIED from [configuration] 2003-12-29 if (base == null) { return null; } // on the first call initialize priorVariables // and add base as the first element if (priorVariables == null) { priorVariables = new ArrayList(); priorVariables.add(base); } int begin = -1; int end = -1; int prec = 0 - END_TOKEN.length(); String variable = null; StringBuffer result = new StringBuffer(); // FIXME: we should probably allow the escaping of the start token while (((begin = base.indexOf(START_TOKEN, prec + END_TOKEN.length())) > -1) && ((end = base.indexOf(END_TOKEN, begin)) > -1)) { result.append(base.substring(prec + END_TOKEN.length(), begin)); variable = base.substring(begin + START_TOKEN.length(), end); // if we've got a loop, create a useful exception message and throw if (priorVariables.contains(variable)) { String initialBase = priorVariables.remove(0).toString(); priorVariables.add(variable); StringBuffer priorVariableSb = new StringBuffer(); // create a nice trace of interpolated variables like so: // var1->var2->var3 for (Iterator it = priorVariables.iterator(); it.hasNext();) { priorVariableSb.append(it.next()); if (it.hasNext()) { priorVariableSb.append("->"); } } throw new IllegalStateException( "infinite loop in property interpolation of " + initialBase + ": " + priorVariableSb.toString()); } // otherwise, add this variable to the interpolation list. else { priorVariables.add(variable); } //QUESTION: getProperty or getPropertyDirect Object value = getProperty(variable); if (value != null) { result.append(interpolateHelper(value.toString(), priorVariables)); // pop the interpolated variable off the stack // this maintains priorVariables correctness for // properties with multiple interpolations, e.g. // prop.name=${some.other.prop1}/blahblah/${some.other.prop2} priorVariables.remove(priorVariables.size() - 1); } else if (defaults != null && defaults.getString(variable, null) != null) { result.append(defaults.getString(variable)); } else { //variable not defined - so put it back in the value result.append(START_TOKEN).append(variable).append(END_TOKEN); } prec = end; } result.append(base.substring(prec + END_TOKEN.length(), base.length())); return result.toString(); }
Inserts a backslash before every comma and backslash.
/** * Inserts a backslash before every comma and backslash. */
private static String escape(String s) { StringBuffer buf = new StringBuffer(s); for (int i = 0; i < buf.length(); i++) { char c = buf.charAt(i); if (c == ',' || c == '\\') { buf.insert(i, '\\'); i++; } } return buf.toString(); }
Removes a backslash from every pair of backslashes.
/** * Removes a backslash from every pair of backslashes. */
private static String unescape(String s) { StringBuffer buf = new StringBuffer(s); for (int i = 0; i < buf.length() - 1; i++) { char c1 = buf.charAt(i); char c2 = buf.charAt(i + 1); if (c1 == '\\' && c2 == '\\') { buf.deleteCharAt(i); } } return buf.toString(); }
Counts the number of successive times 'ch' appears in the 'line' before the position indicated by the 'index'.
/** * Counts the number of successive times 'ch' appears in the * 'line' before the position indicated by the 'index'. */
private static int countPreceding(String line, int index, char ch) { int i; for (i = index - 1; i >= 0; i--) { if (line.charAt(i) != ch) { break; } } return index - 1 - i; }
Checks if the line ends with odd number of backslashes
/** * Checks if the line ends with odd number of backslashes */
private static boolean endsWithSlash(String line) { if (!line.endsWith("\\")) { return false; } return (countPreceding(line, line.length() - 1, '\\') % 2 == 0); }
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.
/** * 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. */
static class PropertiesReader extends LineNumberReader {
Constructor.
Params:
  • reader – A Reader.
/** * Constructor. * * @param reader A Reader. */
public PropertiesReader(Reader reader) { super(reader); }
Read a property.
Throws:
  • IOException – if there is difficulty reading the source.
Returns:a String property
/** * Read a property. * * @return a String property * @throws IOException if there is difficulty reading the source. */
public String readProperty() throws IOException { StringBuffer buffer = new StringBuffer(); String line = readLine(); while (line != null) { line = line.trim(); if ((line.length() != 0) && (line.charAt(0) != '#')) { if (endsWithSlash(line)) { line = line.substring(0, line.length() - 1); buffer.append(line); } else { buffer.append(line); return buffer.toString(); // normal method end } } line = readLine(); } return null; // EOF reached } }
This class divides into tokens a property value. Token separator is "," but commas into the property value are escaped using the backslash in front.
/** * This class divides into tokens a property value. Token * separator is "," but commas into the property value are escaped * using the backslash in front. */
static class PropertiesTokenizer extends StringTokenizer {
The property delimiter used while parsing (a comma).
/** * The property delimiter used while parsing (a comma). */
static final String DELIMITER = ",";
Constructor.
Params:
  • string – A String.
/** * Constructor. * * @param string A String. */
public PropertiesTokenizer(String string) { super(string, DELIMITER); }
Check whether the object has more tokens.
Returns:True if the object has more tokens.
/** * Check whether the object has more tokens. * * @return True if the object has more tokens. */
public boolean hasMoreTokens() { return super.hasMoreTokens(); }
Get next token.
Returns:A String.
/** * Get next token. * * @return A String. */
public String nextToken() { StringBuffer buffer = new StringBuffer(); while (hasMoreTokens()) { String token = super.nextToken(); if (endsWithSlash(token)) { buffer.append(token.substring(0, token.length() - 1)); buffer.append(DELIMITER); } else { buffer.append(token); break; } } return buffer.toString().trim(); } }
Creates an empty extended properties object.
/** * Creates an empty extended properties object. */
public ExtendedProperties() { super(); }
Creates and loads the extended properties from the specified file.
Params:
  • file – the filename to load
Throws:
/** * Creates and loads the extended properties from the specified file. * * @param file the filename to load * @throws IOException if a file error occurs */
public ExtendedProperties(String file) throws IOException { this(file, null); }
Creates and loads the extended properties from the specified file.
Params:
  • file – the filename to load
  • defaultFile – a second filename to load default values from
Throws:
/** * Creates and loads the extended properties from the specified file. * * @param file the filename to load * @param defaultFile a second filename to load default values from * @throws IOException if a file error occurs */
public ExtendedProperties(String file, String defaultFile) throws IOException { this.file = file; basePath = new File(file).getAbsolutePath(); basePath = basePath.substring(0, basePath.lastIndexOf(fileSeparator) + 1); FileInputStream in = null; try { in = new FileInputStream(file); this.load(in); } finally { try { if (in != null) { in.close(); } } catch (IOException ex) {} } if (defaultFile != null) { defaults = new ExtendedProperties(defaultFile); } }
Indicate to client code whether property resources have been initialized or not.
/** * 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".
Returns:A String.
/** * Gets the property value for including other properties files. * By default it is "include". * * @return A String. */
public String getInclude() { return include; }
Sets the property value for including other properties files. By default it is "include".
Params:
  • inc – A String.
/** * Sets the property value for including other properties files. * By default it is "include". * * @param inc A String. */
public void setInclude(String inc) { include = inc; }
Load the properties from the given input stream.
Params:
  • input – the InputStream to load from
Throws:
/** * Load the properties from the given input stream. * * @param input the InputStream to load from * @throws IOException if an IO error occurs */
public void load(InputStream input) throws IOException { load(input, null); }
Load the properties from the given input stream and using the specified encoding.
Params:
  • input – the InputStream to load from
  • enc – the encoding to use
Throws:
/** * Load the properties from the given input stream * and using the specified encoding. * * @param input the InputStream to load from * @param enc the encoding to use * @throws IOException if an IO error occurs */
public synchronized void load(InputStream input, String enc) throws IOException { PropertiesReader reader = null; if (enc != null) { try { reader = new PropertiesReader(new InputStreamReader(input, enc)); } catch (UnsupportedEncodingException ex) { // Another try coming up.... } } if (reader == null) { try { reader = new PropertiesReader(new InputStreamReader(input, "8859_1")); } catch (UnsupportedEncodingException ex) { // ISO8859-1 support is required on java platforms but.... // If it's not supported, use the system default encoding reader = new PropertiesReader(new InputStreamReader(input)); } } try { while (true) { String line = reader.readProperty(); if (line == null) { return; // EOF } 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); } } } } finally { // Loading is initializing isInitialized = true; } }
Gets a property from the configuration.
Params:
  • key – property to retrieve
Returns:value as object. Will return user value if exists, if not then default value if exists, otherwise null
/** * 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 obj = this.get(key); if (obj == null) { // if there isn't a value there, get it from the // defaults if we have them if (defaults != null) { obj = defaults.get(key); } } return obj; }
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"]
Params:
  • key – the key to add
  • value – the value to add
/** * 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 * * <code>resource.loader = file</code> * * is already present in the configuration and you * * <code>addProperty("resource.loader", "classpath")</code> * * Then you will end up with a Vector like the * following: * * <code>["file", "classpath"]</code> * * @param key the key to add * @param value the value to add */
public void addProperty(String key, Object value) { if (value instanceof String) { String str = (String) value; if (str.indexOf(PropertiesTokenizer.DELIMITER) > 0) { // token contains commas, so must be split apart then added PropertiesTokenizer tokenizer = new PropertiesTokenizer(str); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); addPropertyInternal(key, unescape(token)); } } else { // token contains no commas, so can be simply added addPropertyInternal(key, unescape(str)); } } else { addPropertyInternal(key, value); } // Adding a property connotes initialization isInitialized = true; }
Adds a key/value pair to the map. This routine does no magic morphing. It ensures the keylist is maintained
Params:
  • key – the key to store at
  • value – the decoded object to store
/** * Adds a key/value pair to the map. This routine does * no magic morphing. It ensures the keylist is maintained * * @param key the key to store at * @param value the decoded object to store */
private void addPropertyDirect(String key, Object value) { // safety check if (!containsKey(key)) { keysAsListed.add(key); } put(key, value); }
Adds a decoded property to the map w/o checking for commas - used internally when a property has been broken up into strings that could contain escaped commas to prevent the inadvertent vectorization.

Thanks to Leon Messerschmidt for this one.

Params:
  • key – the key to store at
  • value – the decoded object to store
/** * Adds a decoded property to the map w/o checking for commas - used * internally when a property has been broken up into * strings that could contain escaped commas to prevent * the inadvertent vectorization. * <p> * Thanks to Leon Messerschmidt for this one. * * @param key the key to store at * @param value the decoded object to store */
private void addPropertyInternal(String key, Object value) { Object current = this.get(key); if (current instanceof String) { // one object already in map - convert it to a vector List values = new Vector(2); values.add(current); values.add(value); put(key, values); } else if (current instanceof List) { // already a list - just add the new token ((List) current).add(value); } else { // brand new key - store in keysAsListed to retain order if (!containsKey(key)) { keysAsListed.add(key); } put(key, value); } }
Set a property, this will replace any previously set values. Set values is implicitly a call to clearProperty(key), addProperty(key,value).
Params:
  • key – the key to set
  • value – the value to set
/** * Set a property, this will replace any previously * set values. Set values is implicitly a call * to clearProperty(key), addProperty(key,value). * * @param key the key to set * @param value the value to set */
public void setProperty(String key, Object value) { clearProperty(key); addProperty(key, value); }
Save the properties to the given output stream.

The stream is not closed, but it is flushed.

Params:
  • output – an OutputStream, may be null
  • header – a textual comment to act as a file header
Throws:
/** * Save the properties to the given output stream. * <p> * The stream is not closed, but it is flushed. * * @param output an OutputStream, may be null * @param header a textual comment to act as a file header * @throws IOException if an IO error occurs */
public synchronized void save(OutputStream output, String header) throws IOException { if (output == null) { return; } 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(key); if (value != null) { if (value instanceof String) { StringBuffer currentOutput = new StringBuffer(); currentOutput.append(key); currentOutput.append("="); currentOutput.append(escape((String) value)); theWrtr.println(currentOutput.toString()); } else if (value instanceof List) { List values = (List) value; for (Iterator it = values.iterator(); it.hasNext(); ) { String currentElement = (String) it.next(); StringBuffer currentOutput = new StringBuffer(); currentOutput.append(key); currentOutput.append("="); currentOutput.append(escape(currentElement)); theWrtr.println(currentOutput.toString()); } } } theWrtr.println(); theWrtr.flush(); } }
Combines an existing Hashtable with this Hashtable.

Warning: It will overwrite previous entries without warning.

Params:
  • props – the properties to combine
/** * Combines an existing Hashtable with this Hashtable. * <p> * Warning: It will overwrite previous entries without warning. * * @param props the properties to combine */
public void combine(ExtendedProperties props) { for (Iterator it = props.getKeys(); it.hasNext();) { String key = (String) it.next(); setProperty(key, props.get(key)); } }
Clear a property in the configuration.
Params:
  • key – the property key to remove along with corresponding value
/** * Clear a property in the configuration. * * @param key the property 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 (( keysAsListed.get(i)).equals(key)) { keysAsListed.remove(i); break; } } remove(key); } }
Get the list of the keys contained in the configuration repository.
Returns:an Iterator over the keys
/** * Get the list of the keys contained in the configuration * repository. * * @return an Iterator over the keys */
public Iterator getKeys() { return keysAsListed.iterator(); }
Get the list of the keys contained in the configuration repository that match the specified prefix.
Params:
  • prefix – the prefix to match
Returns:an Iterator of keys that match the prefix
/** * Get the list of the keys contained in the configuration * repository that match the specified prefix. * * @param prefix the prefix to match * @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 an ExtendedProperties object that is a subset of this one. Take into account duplicate keys by using the setProperty() in ExtendedProperties.
Params:
  • prefix – the prefix to get a subset for
Returns:a new independent ExtendedProperties
/** * Create an ExtendedProperties object that is a subset * of this one. Take into account duplicate keys * by using the setProperty() in ExtendedProperties. * * @param prefix the prefix to get a subset for * @return a new independent ExtendedProperties */
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; } /* * 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. */ String newKey = null; if (((String) key).length() == prefix.length()) { newKey = prefix; } else { newKey = ((String) key).substring(prefix.length() + 1); } /* * use addPropertyDirect() - this will plug the data as * is into the Map, but will also do the right thing * re key accounting */ c.addPropertyDirect(newKey, get(key)); } } if (validSubset) { return c; } else { return null; } }
Display the configuration for debugging purposes to System.out.
/** * Display the configuration for debugging purposes to System.out. */
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.
Params:
  • key – The configuration key.
Throws:
Returns:The associated string.
/** * Get a string associated with the given configuration key. * * @param key The configuration key. * @return The associated string. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated string if key is found, default value otherwise.
/** * 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. * @throws 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 interpolate((String) value); } else if (value == null) { if (defaults != null) { return interpolate(defaults.getString(key, defaultValue)); } else { return interpolate(defaultValue); } } else if (value instanceof List) { return interpolate((String) ((List) value).get(0)); } else { throw new ClassCastException('\'' + key + "' doesn't map to a String object"); } }
Get a list of properties associated with the given configuration key.
Params:
  • key – The configuration key.
Throws:
Returns:The associated properties if key is found.
/** * Get a list of properties associated with the given * configuration key. * * @param key The configuration key. * @return The associated properties if key is found. * @throws ClassCastException is thrown if the key maps to an * object that is not a String/List. * @throws 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.
Params:
  • key – The configuration key.
Throws:
Returns:The associated properties if key is found.
/** * Get a list of properties associated with the given * configuration key. * * @param key The configuration key. * @return The associated properties if key is found. * @throws ClassCastException is thrown if the key maps to an * object that is not a String/List. * @throws 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.
Params:
  • key – The configuration key.
Throws:
Returns:The associated string array if key is found.
/** * 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. * @throws ClassCastException is thrown if the key maps to an * object that is not a String/List. */
public String[] getStringArray(String key) { Object value = get(key); List values; if (value instanceof String) { values = new Vector(1); values.add(value); } else if (value instanceof List) { values = (List) 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/List object"); } String[] tokens = new String[values.size()]; for (int i = 0; i < tokens.length; i++) { tokens[i] = (String) values.get(i); } return tokens; }
Get a Vector of strings associated with the given configuration key.
Params:
  • key – The configuration key.
Throws:
Returns:The associated Vector.
/** * Get a Vector of strings associated with the given configuration * key. * * @param key The configuration key. * @return The associated Vector. * @throws 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.

The list is a copy of the internal data of this object, and as such you may alter it freely.

Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated Vector.
/** * Get a Vector of strings associated with the given configuration key. * <p> * The list is a copy of the internal data of this object, and as * such you may alter it freely. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated Vector. * @throws 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 List) { return new Vector((List) value); } else if (value instanceof String) { Vector values = new Vector(1); values.add(value); put(key, values); return values; } 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 List of strings associated with the given configuration key.

The list is a copy of the internal data of this object, and as such you may alter it freely.

Params:
  • key – The configuration key.
Throws:
Returns:The associated List object.
Since:Commons Collections 3.2
/** * Get a List of strings associated with the given configuration key. * <p> * The list is a copy of the internal data of this object, and as * such you may alter it freely. * * @param key The configuration key. * @return The associated List object. * @throws ClassCastException is thrown if the key maps to an * object that is not a List. * @since Commons Collections 3.2 */
public List getList(String key) { return getList(key, null); }
Get a List of strings associated with the given configuration key.

The list is a copy of the internal data of this object, and as such you may alter it freely.

Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated List.
Since:Commons Collections 3.2
/** * Get a List of strings associated with the given configuration key. * <p> * The list is a copy of the internal data of this object, and as * such you may alter it freely. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated List. * @throws ClassCastException is thrown if the key maps to an * object that is not a List. * @since Commons Collections 3.2 */
public List getList(String key, List defaultValue) { Object value = get(key); if (value instanceof List) { return new ArrayList((List) value); } else if (value instanceof String) { List values = new ArrayList(1); values.add(value); put(key, values); return values; } else if (value == null) { if (defaults != null) { return defaults.getList(key, defaultValue); } else { return ((defaultValue == null) ? new ArrayList() : defaultValue); } } else { throw new ClassCastException('\'' + key + "' doesn't map to a List object"); } }
Get a boolean associated with the given configuration key.
Params:
  • key – The configuration key.
Throws:
Returns:The associated boolean.
/** * Get a boolean associated with the given configuration key. * * @param key The configuration key. * @return The associated boolean. * @throws NoSuchElementException is thrown if the key doesn't * map to an existing object. * @throws ClassCastException is thrown if the key maps to an * object that is not a Boolean. */
public boolean getBoolean(String key) { Boolean b = getBoolean(key, 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated boolean.
/** * Get a boolean associated with the given configuration key. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated boolean. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated boolean if key is found and has valid format, default value otherwise.
/** * 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. * @throws 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.
Params:
  • value – the value to test for boolean state
Returns:true or false if the supplied text maps to a boolean value, or null otherwise.
/** * Test whether the string represent by value maps to a boolean * value or not. We will allow <code>true</code>, <code>on</code>, * and <code>yes</code> for a <code>true</code> boolean value, and * <code>false</code>, <code>off</code>, and <code>no</code> for * <code>false</code> boolean values. Case of value to test for * boolean status is ignored. * * @param value the value to test for boolean state * @return <code>true</code> or <code>false</code> if the supplied * text maps to a boolean value, or <code>null</code> otherwise. */
public String testBoolean(String value) { String s = 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.
Params:
  • key – The configuration key.
Throws:
Returns:The associated byte.
/** * Get a byte associated with the given configuration key. * * @param key The configuration key. * @return The associated byte. * @throws NoSuchElementException is thrown if the key doesn't * map to an existing object. * @throws ClassCastException is thrown if the key maps to an * object that is not a Byte. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated byte.
/** * Get a byte associated with the given configuration key. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated byte. * @throws ClassCastException is thrown if the key maps to an * object that is not a Byte. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated byte if key is found and has valid format, default value otherwise.
/** * 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. * @throws ClassCastException is thrown if the key maps to an * object that is not a Byte. * @throws 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.
Params:
  • key – The configuration key.
Throws:
Returns:The associated short.
/** * Get a short associated with the given configuration key. * * @param key The configuration key. * @return The associated short. * @throws NoSuchElementException is thrown if the key doesn't * map to an existing object. * @throws ClassCastException is thrown if the key maps to an * object that is not a Short. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated short.
/** * Get a short associated with the given configuration key. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated short. * @throws ClassCastException is thrown if the key maps to an * object that is not a Short. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated short if key is found and has valid format, default value otherwise.
/** * 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. * @throws ClassCastException is thrown if the key maps to an * object that is not a Short. * @throws 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.
Params:
  • name – The resource name.
Returns:The value of the resource as an integer.
/** * 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.
Params:
  • name – The resource name
  • def – The default value of the resource.
Returns:The value of the resource as an integer.
/** * 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.
Params:
  • key – The configuration key.
Throws:
Returns:The associated int.
/** * Get a int associated with the given configuration key. * * @param key The configuration key. * @return The associated int. * @throws NoSuchElementException is thrown if the key doesn't * map to an existing object. * @throws ClassCastException is thrown if the key maps to an * object that is not a Integer. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated int.
/** * Get a int associated with the given configuration key. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated int. * @throws ClassCastException is thrown if the key maps to an * object that is not a Integer. * @throws NumberFormatException is thrown if the value mapped * by the key has not a valid number format. */
public int getInteger(String key, int defaultValue) { Integer i = getInteger(key, null); if (i == null) { return defaultValue; } return i.intValue(); }
Get a int associated with the given configuration key.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated int if key is found and has valid format, default value otherwise.
/** * 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. * @throws ClassCastException is thrown if the key maps to an * object that is not a Integer. * @throws 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.
Params:
  • key – The configuration key.
Throws:
Returns:The associated long.
/** * Get a long associated with the given configuration key. * * @param key The configuration key. * @return The associated long. * @throws NoSuchElementException is thrown if the key doesn't * map to an existing object. * @throws ClassCastException is thrown if the key maps to an * object that is not a Long. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated long.
/** * Get a long associated with the given configuration key. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated long. * @throws ClassCastException is thrown if the key maps to an * object that is not a Long. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated long if key is found and has valid format, default value otherwise.
/** * 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. * @throws ClassCastException is thrown if the key maps to an * object that is not a Long. * @throws 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.
Params:
  • key – The configuration key.
Throws:
Returns:The associated float.
/** * Get a float associated with the given configuration key. * * @param key The configuration key. * @return The associated float. * @throws NoSuchElementException is thrown if the key doesn't * map to an existing object. * @throws ClassCastException is thrown if the key maps to an * object that is not a Float. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated float.
/** * Get a float associated with the given configuration key. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated float. * @throws ClassCastException is thrown if the key maps to an * object that is not a Float. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated float if key is found and has valid format, default value otherwise.
/** * 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. * @throws ClassCastException is thrown if the key maps to an * object that is not a Float. * @throws 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.
Params:
  • key – The configuration key.
Throws:
Returns:The associated double.
/** * Get a double associated with the given configuration key. * * @param key The configuration key. * @return The associated double. * @throws NoSuchElementException is thrown if the key doesn't * map to an existing object. * @throws ClassCastException is thrown if the key maps to an * object that is not a Double. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated double.
/** * Get a double associated with the given configuration key. * * @param key The configuration key. * @param defaultValue The default value. * @return The associated double. * @throws ClassCastException is thrown if the key maps to an * object that is not a Double. * @throws 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.
Params:
  • key – The configuration key.
  • defaultValue – The default value.
Throws:
Returns:The associated double if key is found and has valid format, default value otherwise.
/** * 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. * @throws ClassCastException is thrown if the key maps to an * object that is not a Double. * @throws 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.

NOTE: From Commons Collections 3.2 this method will pick up any default parent Properties of the specified input object.

Params:
  • props – the properties object to convert
Returns:new ExtendedProperties created from props
/** * Convert a standard properties class into a configuration class. * <p> * NOTE: From Commons Collections 3.2 this method will pick up * any default parent Properties of the specified input object. * * @param props the properties object to convert * @return new ExtendedProperties created from props */
public static ExtendedProperties convertProperties(Properties props) { ExtendedProperties c = new ExtendedProperties(); for (Enumeration e = props.propertyNames(); e.hasMoreElements();) { String s = (String) e.nextElement(); c.setProperty(s, props.getProperty(s)); } return c; } }