You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2016/07/17 10:46:07 UTC
svn commit: r1753054 [2/2] - in /velocity/engine/trunk: src/changes/
velocity-engine-core/src/main/java/org/apache/velocity/app/
velocity-engine-core/src/main/java/org/apache/velocity/runtime/
velocity-engine-core/src/main/java/org/apache/velocity/runt...
Added: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/ExtProperties.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/ExtProperties.java?rev=1753054&view=auto
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/ExtProperties.java (added)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/ExtProperties.java Sun Jul 17 10:46:07 2016
@@ -0,0 +1,1725 @@
+/*
+ * 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.velocity.util;
+
+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.Map;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+import java.util.Set;
+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.
+ * <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!
+ *
+ * This class is a clone of org.apache.commons.collections.ExtendedProperties
+ * (which has been removed from commons-collections-4.0)
+ *
+ * @since 2.0
+ * @version $Revision: $
+ * @version $Id: ExtProperties.java$
+ *
+ * @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>
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class ExtProperties extends Hashtable<String,Object> {
+
+ /**
+ * Default configurations repository.
+ */
+ private ExtProperties 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 ExtProperties object.
+ */
+ protected String basePath;
+
+ /**
+ * 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.
+ */
+ 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();
+
+ protected final static String START_TOKEN="${";
+ protected final static String END_TOKEN="}";
+
+
+ /**
+ * 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.
+ *
+ * @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.
+ */
+ 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.
+ */
+ 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'.
+ */
+ 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
+ */
+ 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.
+ */
+ static class PropertiesReader extends LineNumberReader {
+ /**
+ * Constructor.
+ *
+ * @param reader A Reader.
+ */
+ public PropertiesReader(Reader reader) {
+ super(reader);
+ }
+
+ /**
+ * 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.
+ */
+ static class PropertiesTokenizer extends StringTokenizer {
+ /**
+ * The property delimiter used while parsing (a comma).
+ */
+ static final String DELIMITER = ",";
+
+ /**
+ * Constructor.
+ *
+ * @param string A String.
+ */
+ public PropertiesTokenizer(String string) {
+ super(string, DELIMITER);
+ }
+
+ /**
+ * 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 (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.
+ */
+ public ExtProperties() {
+ super();
+ }
+
+ /**
+ * Creates and loads the extended properties from the specified file.
+ *
+ * @param file the filename to load
+ * @throws IOException if a file error occurs
+ */
+ public ExtProperties(String file) throws IOException {
+ this(file, null);
+ }
+
+ /**
+ * 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 ExtProperties(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 ExtProperties(defaultFile);
+ }
+ }
+
+ /**
+ * 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 include;
+ }
+
+ /**
+ * 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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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
+ *
+ * <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
+ *
+ * @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.
+ * <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).
+ *
+ * @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.
+ * <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.
+ * <p>
+ * Warning: It will overwrite previous entries without warning.
+ *
+ * @param props the properties to combine
+ */
+ public void combine(ExtProperties props) {
+ for (Iterator it = props.getKeys(); it.hasNext();) {
+ String key = (String) it.next();
+ setProperty(key, props.get(key));
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @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.
+ *
+ * @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 ExtProperties object that is a subset
+ * of this one. Take into account duplicate keys
+ * by using the setProperty() in ExtProperties.
+ *
+ * @param prefix the prefix to get a subset for
+ * @return a new independent ExtProperties
+ */
+ public ExtProperties subset(String prefix) {
+ ExtProperties c = new ExtProperties();
+ 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.
+ */
+ 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.
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ * <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.
+ * <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.
+ * <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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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 <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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ *
+ * @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.
+ * <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 ExtProperties created from props
+ */
+ public static ExtProperties convertProperties(Properties props) {
+ ExtProperties c = new ExtProperties();
+
+ for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
+ String s = (String) e.nextElement();
+ c.setProperty(s, props.getProperty(s));
+ }
+
+ return c;
+ }
+
+ /**
+ * Convert a Map 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 Map object to convert
+ * @return new ExtProperties created from props
+ */
+ public static ExtProperties convertProperties(Map props) {
+ ExtProperties c = new ExtProperties();
+
+ for (Map.Entry entry : (Set<Map.Entry>)props.entrySet())
+ {
+ c.setProperty(String.valueOf(entry.getKey()), entry.getValue());
+ }
+ return c;
+ }
+
+}
Modified: velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingResourceLoader.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingResourceLoader.java?rev=1753054&r1=1753053&r2=1753054&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingResourceLoader.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingResourceLoader.java Sun Jul 17 10:46:07 2016
@@ -21,10 +21,10 @@ package org.apache.velocity.test.misc;
import java.io.Reader;
-import org.apache.commons.collections.ExtendedProperties;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.resource.Resource;
import org.apache.velocity.runtime.resource.loader.ResourceLoader2;
+import org.apache.velocity.util.ExtProperties;
/**
* Resource Loader that always throws an exception. Used to test
@@ -36,7 +36,7 @@ import org.apache.velocity.runtime.resou
public class ExceptionGeneratingResourceLoader extends ResourceLoader2
{
- public void init(ExtendedProperties configuration)
+ public void init(ExtProperties configuration)
{
}