📄 abstractconfiguration.java
字号:
/* * AbstractConfiguration.java * * Created on 2006年7月18日, 下午12:16 * * To change this template, choose Tools | Options and locate the template under * the Source Creation and Management node. Right-click the template and choose * Open. You can then make changes to the template in the Source Editor. */package tot.xml;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 * * QUESTION: getProperty or getPropertyDirect */ Object value = getProperty((String) key); if (value instanceof String) { c.addPropertyDirect(newKey, interpolate((String) value)); } else { c.addProperty(newKey, value); } } } if (validSubset) { return c; } else { return null; } } /** * Check if the configuration is empty * * @return <code>true</code> if Configuration is empty, * <code>false</code> otherwise. */ public abstract boolean isEmpty(); /** * check if the configuration contains the key * * @param key the configuration key * * @return <code>true</code> if Configuration contain given key, * <code>false</code> otherwise. */ public abstract boolean containsKey(String key); /** * 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 configuration key * @param value the property value */ public void setProperty(String key, Object value) { clearProperty(key); addProperty(key, value); // QUESTION: or addPropertyDirect? } /** * Clear a property in the configuration. * * @param key the key to remove along with corresponding value. */ public abstract void clearProperty(String key); /** * Get the list of the keys contained in the configuration * repository. * * @return An Iterator. */ public abstract Iterator getKeys(); /** * Get the list of the keys contained in the configuration * repository that match the specified prefix. * * @param prefix The prefix to test against. * * @return An Iterator of keys that match the prefix. */ public Iterator getKeys(String prefix) { Iterator keys = getKeys(); ArrayList matchingKeys = new ArrayList(); while (keys.hasNext()) { Object key = keys.next(); if (key instanceof String && ((String) key).startsWith(prefix)) { matchingKeys.add(key); } } return matchingKeys.iterator(); } /** * 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/Vector. * @throws IllegalArgumentException if one of the tokens is * malformed (does not contain an equals sign). * * @see #getProperties(String, Properties) */ public Properties getProperties(String key) { return getProperties(key, null); } /** * Get a list of properties associated with the given * configuration key. * * @param key The configuration key. * @param defaults Any default values for the returned * <code>Properties</code> object. Ignored if <code>null</code>. * * @return The associated properties if key is found. * * @throws ClassCastException is thrown if the key maps to an * object that is not a String/Vector of Strings. * @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 = (defaults == null ? new Properties() : 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 if (tokens.length == 1 && "".equals(token)) { // Semantically equivalent to an empty Properties // object. break; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -