?? abstractconfiguration.java
字號:
package net.myvietnam.mvncore.configuration;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowledgement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgement may appear in the software itself,
* if and wherever such third-party acknowledgements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Abstract configuration class. Provide basic functionality but does not
* store any data. If you want to write your own Configuration class
* then you should implement only abstract methods from this class.
*
* @author <a href="mailto:ksh@scand.com">Konstantin Shaposhnikov</a>
* @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a>
* @version $Id: AbstractConfiguration.java,v 1.4 2004/06/01 13:25:39 skoehler Exp $
*/
public abstract class AbstractConfiguration implements Configuration
{
/** how big the initial arraylist for splitting up name value pairs */
private static final int INITIAL_LIST_SIZE = 2;
private static Log log = LogFactory.getLog(AbstractConfiguration.class);
/**
* stores the configuration key-value pairs
*/
protected Configuration defaults = null;
/** start token */
protected static final String START_TOKEN = "${";
/** end token */
protected static final String END_TOKEN = "}";
/**
* Empty constructor.
*/
public AbstractConfiguration()
{
}
/**
* Creates an empty AbstractConfiguration object with
* a Super-Object which is queries for every key.
*
* @param defaults Configuration defaults to use if key not in file
*/
public AbstractConfiguration(Configuration defaults)
{
this();
this.defaults = defaults;
}
/**
* Add a property to the configuration. If it already exists then the value
* stated here will be added to the configuration entry. For example, if
*
* resource.loader = file
*
* is already present in the configuration and you
*
* addProperty("resource.loader", "classpath")
*
* Then you will end up with a Vector like the following:
*
* ["file", "classpath"]
*
* @param key The Key to add the property to.
* @param token The Value to add.
*/
public void addProperty(String key, Object token)
{
if (token instanceof String)
{
for(Iterator it = processString((String) token).iterator();
it.hasNext();)
{
addPropertyDirect(key, it.next());
}
}
else if (token instanceof Collection)
{
for (Iterator it = ((Collection) token).iterator(); it.hasNext();)
{
addProperty(key, it.next());
}
}
else
{
addPropertyDirect(key, token);
}
}
/**
* Read property. Should return <code>null</code> if the key doesn't
* map to an existing object.
*
* @param key key to use for mapping
*
* @return object associated with the given configuration key.
*/
protected abstract Object getPropertyDirect(String key);
/**
* Adds a key/value pair to the Configuration. Override this method to
* provide write acces to underlying Configuration store.
*
* @param key key to use for mapping
* @param obj object to store
*/
protected abstract void addPropertyDirect(String key, Object obj);
/**
* 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)
{
return (interpolateHelper(base, null));
}
/**
* Recursive handler for multple 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)
{
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();
}
/**
* Returns a Vector of Strings built from the supplied
* String. Splits up CSV lists. If no commas are in the
* String, simply returns a Vector with the String as its
* first element
*
* @param token The String to tokenize
*
* @return A List of Strings
*/
protected List processString(String token)
{
List retList = new ArrayList(INITIAL_LIST_SIZE);
if (token.indexOf(PropertiesTokenizer.DELIMITER) > 0)
{
PropertiesTokenizer tokenizer =
new PropertiesTokenizer(token);
while (tokenizer.hasMoreTokens())
{
String value = tokenizer.nextToken();
retList.add(value);
}
}
else
{
retList.add(token);
}
//
// We keep the sequence of the keys here and
// we also keep it in the Container. So the
// Keys are added to the store in the sequence that
// is given in the properties
return retList;
}
/**
* 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.
*/
protected final Boolean testBoolean(String value)
{
String s = value.toLowerCase();
if (s.equals("true") || s.equals("on") || s.equals("yes"))
{
return Boolean.TRUE;
}
else if (s.equals("false") || s.equals("off") || s.equals("no"))
{
return Boolean.FALSE;
}
else
{
return null;
}
}
/**
* Create an BaseConfiguration object that is a subset
* of this one.
*
* @param prefix prefix string for keys
*
* @return subset of configuration if there is keys, that match
* given prefix, or <code>null</code> if there is no such keys.
*/
public Configuration subset(String prefix)
{
BaseConfiguration c = new BaseConfiguration();
Iterator keys = this.getKeys();
boolean validSubset = false;
while (keys.hasNext())
{
Object key = keys.next();
if (key instanceof String && ((String) key).startsWith(prefix))
{
if (!validSubset)
{
validSubset = true;
}
String newKey = null;
/*
* Check to make sure that c.subset(prefix) doesn't blow up when
* there is only a single property with the key prefix. This is
* not a useful subset but it is a valid subset.
*/
if (((String) key).length() == prefix.length())
{
newKey = prefix;
}
else
{
newKey = ((String) key).substring(prefix.length() + 1);
}
/*
* use addPropertyDirect() - this will plug the data as is into
* the Map, but will also do the right thing re key accounting
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -