0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.commons.configuration;
0019:
0020: import java.math.BigDecimal;
0021: import java.math.BigInteger;
0022: import java.util.ArrayList;
0023: import java.util.Iterator;
0024: import java.util.List;
0025: import java.util.NoSuchElementException;
0026: import java.util.Properties;
0027:
0028: import org.apache.commons.collections.Predicate;
0029: import org.apache.commons.collections.iterators.FilterIterator;
0030: import org.apache.commons.configuration.event.ConfigurationErrorEvent;
0031: import org.apache.commons.configuration.event.ConfigurationErrorListener;
0032: import org.apache.commons.configuration.event.EventSource;
0033: import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
0034: import org.apache.commons.lang.BooleanUtils;
0035: import org.apache.commons.lang.text.StrLookup;
0036: import org.apache.commons.lang.text.StrSubstitutor;
0037: import org.apache.commons.logging.Log;
0038: import org.apache.commons.logging.impl.NoOpLog;
0039:
0040: /**
0041: * <p>Abstract configuration class. Provides basic functionality but does not
0042: * store any data.</p>
0043: * <p>If you want to write your own Configuration class then you should
0044: * implement only abstract methods from this class. A lot of functionality
0045: * needed by typical implementations of the <code>Configuration</conde>
0046: * interface is already provided by this base class. Following is a list of
0047: * feauters implemented here:
0048: * <ul><li>Data conversion support. The various data types required by the
0049: * <code>Configuration</code> interface are already handled by this base class.
0050: * A concrete sub class only needs to provide a generic <code>getProperty()</code>
0051: * method.</li>
0052: * <li>Support for variable interpolation. Property values containing special
0053: * variable tokens (like <code>${var}</code>) will be replaced by their
0054: * corresponding values.</li>
0055: * <li>Support for string lists. The values of properties to be added to this
0056: * configuration are checked whether they contain a list delimiter character. If
0057: * this is the case and if list splitting is enabled, the string is splitted and
0058: * multiple values are added for this property. (With the
0059: * <code>setListDelimiter()</code> method the delimiter character can be
0060: * specified; per default a comma is used. The
0061: * <code>setDelimiterParsingDisabled()</code> method can be used to disable
0062: * list splitting completely.)</li>
0063: * <li>Allows to specify how missing properties are treated. Per default the
0064: * get methods returning an object will return <b>null</b> if the searched
0065: * property key is not found (and no default value is provided). With the
0066: * <code>setThrowExceptionOnMissing()</code> method this behavior can be
0067: * changed to throw an exception when a requested property cannot be found.</li>
0068: * <li>Basic event support. Whenever this configuration is modified registered
0069: * event listeners are notified. Refer to the various <code>EVENT_XXX</code>
0070: * constants to get an impression about which event types are supported.</li>
0071: * </ul></p>
0072: *
0073: * @author <a href="mailto:ksh@scand.com">Konstantin Shaposhnikov </a>
0074: * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger </a>
0075: * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen </a>
0076: * @version $Id: AbstractConfiguration.java,v 1.29 2004/12/02 22:05:52 ebourg
0077: * Exp $
0078: */
0079: public abstract class AbstractConfiguration extends EventSource
0080: implements Configuration {
0081: /**
0082: * Constant for the add property event type.
0083: * @since 1.3
0084: */
0085: public static final int EVENT_ADD_PROPERTY = 1;
0086:
0087: /**
0088: * Constant for the clear property event type.
0089: * @since 1.3
0090: */
0091: public static final int EVENT_CLEAR_PROPERTY = 2;
0092:
0093: /**
0094: * Constant for the set property event type.
0095: * @since 1.3
0096: */
0097: public static final int EVENT_SET_PROPERTY = 3;
0098:
0099: /**
0100: * Constant for the clear configuration event type.
0101: * @since 1.3
0102: */
0103: public static final int EVENT_CLEAR = 4;
0104:
0105: /**
0106: * Constant for the get property event type. This event type is used for
0107: * error events.
0108: * @since 1.4
0109: */
0110: public static final int EVENT_READ_PROPERTY = 5;
0111:
0112: /** start token */
0113: protected static final String START_TOKEN = "${";
0114:
0115: /** end token */
0116: protected static final String END_TOKEN = "}";
0117:
0118: /** The default value for listDelimiter */
0119: private static char defaultListDelimiter = ',';
0120:
0121: /** Delimiter used to convert single values to lists */
0122: private char listDelimiter = defaultListDelimiter;
0123:
0124: /**
0125: * When set to true the given configuration delimiter will not be used
0126: * while parsing for this configuration.
0127: */
0128: private boolean delimiterParsingDisabled;
0129:
0130: /**
0131: * Whether the configuration should throw NoSuchElementExceptions or simply
0132: * return null when a property does not exist. Defaults to return null.
0133: */
0134: private boolean throwExceptionOnMissing;
0135:
0136: /** Stores a reference to the object that handles variable interpolation.*/
0137: private StrSubstitutor substitutor;
0138:
0139: /** Stores the logger.*/
0140: private Log log;
0141:
0142: /**
0143: * Creates a new instance of <code>AbstractConfiguration</code>.
0144: */
0145: public AbstractConfiguration() {
0146: setLogger(null);
0147: }
0148:
0149: /**
0150: * For configurations extending AbstractConfiguration, allow them to change
0151: * the listDelimiter from the default comma (","). This value will be used
0152: * only when creating new configurations. Those already created will not be
0153: * affected by this change
0154: *
0155: * @param delimiter The new listDelimiter
0156: */
0157: public static void setDefaultListDelimiter(char delimiter) {
0158: AbstractConfiguration.defaultListDelimiter = delimiter;
0159: }
0160:
0161: /**
0162: * Sets the default list delimiter.
0163: *
0164: * @param delimiter the delimiter character
0165: * @deprecated Use AbstractConfiguration.setDefaultListDelimiter(char)
0166: * instead
0167: */
0168: public static void setDelimiter(char delimiter) {
0169: setDefaultListDelimiter(delimiter);
0170: }
0171:
0172: /**
0173: * Retrieve the current delimiter. By default this is a comma (",").
0174: *
0175: * @return The delimiter in use
0176: */
0177: public static char getDefaultListDelimiter() {
0178: return AbstractConfiguration.defaultListDelimiter;
0179: }
0180:
0181: /**
0182: * Returns the default list delimiter.
0183: *
0184: * @return the default list delimiter
0185: * @deprecated Use AbstractConfiguration.getDefaultListDelimiter() instead
0186: */
0187: public static char getDelimiter() {
0188: return getDefaultListDelimiter();
0189: }
0190:
0191: /**
0192: * Change the list delimiter for this configuration.
0193: *
0194: * Note: this change will only be effective for new parsings. If you
0195: * want it to take effect for all loaded properties use the no arg constructor
0196: * and call this method before setting the source.
0197: *
0198: * @param listDelimiter The new listDelimiter
0199: */
0200: public void setListDelimiter(char listDelimiter) {
0201: this .listDelimiter = listDelimiter;
0202: }
0203:
0204: /**
0205: * Retrieve the delimiter for this configuration. The default
0206: * is the value of defaultListDelimiter.
0207: *
0208: * @return The listDelimiter in use
0209: */
0210: public char getListDelimiter() {
0211: return listDelimiter;
0212: }
0213:
0214: /**
0215: * Determine if this configuration is using delimiters when parsing
0216: * property values to convert them to lists of values. Defaults to false
0217: * @return true if delimiters are not being used
0218: */
0219: public boolean isDelimiterParsingDisabled() {
0220: return delimiterParsingDisabled;
0221: }
0222:
0223: /**
0224: * Set whether this configuration should use delimiters when parsing
0225: * property values to convert them to lists of values. By default delimiter
0226: * parsing is enabled
0227: *
0228: * Note: this change will only be effective for new parsings. If you
0229: * want it to take effect for all loaded properties use the no arg constructor
0230: * and call this method before setting source.
0231: * @param delimiterParsingDisabled a flag whether delimiter parsing should
0232: * be disabled
0233: */
0234: public void setDelimiterParsingDisabled(
0235: boolean delimiterParsingDisabled) {
0236: this .delimiterParsingDisabled = delimiterParsingDisabled;
0237: }
0238:
0239: /**
0240: * Allows to set the <code>throwExceptionOnMissing</code> flag. This
0241: * flag controls the behavior of property getter methods that return
0242: * objects if the requested property is missing. If the flag is set to
0243: * <b>false</b> (which is the default value), these methods will return
0244: * <b>null</b>. If set to <b>true</b>, they will throw a
0245: * <code>NoSuchElementException</code> exception. Note that getter methods
0246: * for primitive data types are not affected by this flag.
0247: *
0248: * @param throwExceptionOnMissing The new value for the property
0249: */
0250: public void setThrowExceptionOnMissing(
0251: boolean throwExceptionOnMissing) {
0252: this .throwExceptionOnMissing = throwExceptionOnMissing;
0253: }
0254:
0255: /**
0256: * Returns true if missing values throw Exceptions.
0257: *
0258: * @return true if missing values throw Exceptions
0259: */
0260: public boolean isThrowExceptionOnMissing() {
0261: return throwExceptionOnMissing;
0262: }
0263:
0264: /**
0265: * Returns the object that is responsible for variable interpolation.
0266: *
0267: * @return the object responsible for variable interpolation
0268: * @since 1.4
0269: */
0270: public synchronized StrSubstitutor getSubstitutor() {
0271: if (substitutor == null) {
0272: substitutor = new StrSubstitutor(createInterpolator());
0273: }
0274: return substitutor;
0275: }
0276:
0277: /**
0278: * Returns the <code>ConfigurationInterpolator</code> object that manages
0279: * the lookup objects for resolving variables. <em>Note:</em> If this
0280: * object is manipulated (e.g. new lookup objects added), synchronisation
0281: * has to be manually ensured. Because
0282: * <code>ConfigurationInterpolator</code> is not thread-safe concurrent
0283: * access to properties of this configuration instance (which causes the
0284: * interpolator to be invoked) may cause race conditions.
0285: *
0286: * @return the <code>ConfigurationInterpolator</code> associated with this
0287: * configuration
0288: * @since 1.4
0289: */
0290: public ConfigurationInterpolator getInterpolator() {
0291: return (ConfigurationInterpolator) getSubstitutor()
0292: .getVariableResolver();
0293: }
0294:
0295: /**
0296: * Creates the interpolator object that is responsible for variable
0297: * interpolation. This method is invoked on first access of the
0298: * interpolation features. It creates a new instance of
0299: * <code>ConfigurationInterpolator</code> and sets the default lookup
0300: * object to an implementation that queries this configuration.
0301: *
0302: * @return the newly created interpolator object
0303: * @since 1.4
0304: */
0305: protected ConfigurationInterpolator createInterpolator() {
0306: ConfigurationInterpolator interpol = new ConfigurationInterpolator();
0307: interpol.setDefaultLookup(new StrLookup() {
0308: public String lookup(String var) {
0309: Object prop = resolveContainerStore(var);
0310: return (prop != null) ? prop.toString() : null;
0311: }
0312: });
0313: return interpol;
0314: }
0315:
0316: /**
0317: * Returns the logger used by this configuration object.
0318: *
0319: * @return the logger
0320: * @since 1.4
0321: */
0322: public Log getLogger() {
0323: return log;
0324: }
0325:
0326: /**
0327: * Allows to set the logger to be used by this configuration object. This
0328: * method makes it possible for clients to exactly control logging behavior.
0329: * Per default a logger is set that will ignore all log messages. Derived
0330: * classes that want to enable logging should call this method during their
0331: * initialization with the logger to be used.
0332: *
0333: * @param log the new logger
0334: * @since 1.4
0335: */
0336: public void setLogger(Log log) {
0337: this .log = (log != null) ? log : new NoOpLog();
0338: }
0339:
0340: /**
0341: * Adds a special
0342: * <code>{@link org.apache.commons.configuration.event.ConfigurationErrorListener}</code>
0343: * object to this configuration that will log all internal errors. This
0344: * method is intended to be used by certain derived classes, for which it is
0345: * known that they can fail on property access (e.g.
0346: * <code>DatabaseConfiguration</code>).
0347: *
0348: * @since 1.4
0349: */
0350: public void addErrorLogListener() {
0351: addErrorListener(new ConfigurationErrorListener() {
0352: public void configurationError(ConfigurationErrorEvent event) {
0353: getLogger().warn("Internal error", event.getCause());
0354: }
0355: });
0356: }
0357:
0358: /**
0359: * {@inheritDoc}
0360: */
0361: public void addProperty(String key, Object value) {
0362: fireEvent(EVENT_ADD_PROPERTY, key, value, true);
0363:
0364: if (!isDelimiterParsingDisabled()) {
0365: Iterator it = PropertyConverter.toIterator(value,
0366: getListDelimiter());
0367: while (it.hasNext()) {
0368: addPropertyDirect(key, it.next());
0369: }
0370: } else {
0371: addPropertyDirect(key, value);
0372: }
0373:
0374: fireEvent(EVENT_ADD_PROPERTY, key, value, false);
0375: }
0376:
0377: /**
0378: * Adds a key/value pair to the Configuration. Override this method to
0379: * provide write acces to underlying Configuration store.
0380: *
0381: * @param key key to use for mapping
0382: * @param value object to store
0383: */
0384: protected abstract void addPropertyDirect(String key, Object value);
0385:
0386: /**
0387: * interpolate key names to handle ${key} stuff
0388: *
0389: * @param base string to interpolate
0390: *
0391: * @return returns the key name with the ${key} substituted
0392: */
0393: protected String interpolate(String base) {
0394: Object result = interpolate((Object) base);
0395: return (result == null) ? null : result.toString();
0396: }
0397:
0398: /**
0399: * Returns the interpolated value. Non String values are returned without change.
0400: *
0401: * @param value the value to interpolate
0402: *
0403: * @return returns the value with variables substituted
0404: */
0405: protected Object interpolate(Object value) {
0406: return PropertyConverter.interpolate(value, this );
0407: }
0408:
0409: /**
0410: * Recursive handler for multple levels of interpolation.
0411: *
0412: * When called the first time, priorVariables should be null.
0413: *
0414: * @param base string with the ${key} variables
0415: * @param priorVariables serves two purposes: to allow checking for loops,
0416: * and creating a meaningful exception message should a loop occur. It's
0417: * 0'th element will be set to the value of base from the first call. All
0418: * subsequent interpolated variables are added afterward.
0419: *
0420: * @return the string with the interpolation taken care of
0421: * @deprecated Interpolation is now handled by
0422: * <code>{@link PropertyConverter}</code>; this method will no longer be
0423: * called
0424: */
0425: protected String interpolateHelper(String base, List priorVariables) {
0426: return base; // just a dummy implementation
0427: }
0428:
0429: /**
0430: * {@inheritDoc}
0431: */
0432: public Configuration subset(String prefix) {
0433: return new SubsetConfiguration(this , prefix, ".");
0434: }
0435:
0436: /**
0437: * {@inheritDoc}
0438: */
0439: public abstract boolean isEmpty();
0440:
0441: /**
0442: * {@inheritDoc}
0443: */
0444: public abstract boolean containsKey(String key);
0445:
0446: /**
0447: * {@inheritDoc}
0448: */
0449: public void setProperty(String key, Object value) {
0450: fireEvent(EVENT_SET_PROPERTY, key, value, true);
0451: setDetailEvents(false);
0452: try {
0453: clearProperty(key);
0454: addProperty(key, value);
0455: } finally {
0456: setDetailEvents(true);
0457: }
0458: fireEvent(EVENT_SET_PROPERTY, key, value, false);
0459: }
0460:
0461: /**
0462: * Removes the specified property from this configuration. This
0463: * implementation performs some preparations and then delegates to
0464: * <code>clearPropertyDirect()</code>, which will do the real work.
0465: *
0466: * @param key the key to be removed
0467: */
0468: public void clearProperty(String key) {
0469: fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
0470: clearPropertyDirect(key);
0471: fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
0472: }
0473:
0474: /**
0475: * Removes the specified property from this configuration. This method is
0476: * called by <code>clearProperty()</code> after it has done some
0477: * preparations. It should be overriden in sub classes. This base
0478: * implementation is just left empty.
0479: *
0480: * @param key the key to be removed
0481: */
0482: protected void clearPropertyDirect(String key) {
0483: // override in sub classes
0484: }
0485:
0486: /**
0487: * {@inheritDoc}
0488: */
0489: public void clear() {
0490: fireEvent(EVENT_CLEAR, null, null, true);
0491: setDetailEvents(false);
0492: try {
0493: Iterator it = getKeys();
0494: while (it.hasNext()) {
0495: String key = (String) it.next();
0496: it.remove();
0497:
0498: if (containsKey(key)) {
0499: // workaround for Iterators that do not remove the property on calling remove()
0500: clearProperty(key);
0501: }
0502: }
0503: } finally {
0504: setDetailEvents(true);
0505: }
0506: fireEvent(EVENT_CLEAR, null, null, false);
0507: }
0508:
0509: /**
0510: * {@inheritDoc}
0511: */
0512: public abstract Iterator getKeys();
0513:
0514: /**
0515: * {@inheritDoc}
0516: */
0517: public Iterator getKeys(final String prefix) {
0518: return new FilterIterator(getKeys(), new Predicate() {
0519: public boolean evaluate(Object obj) {
0520: String key = (String) obj;
0521: return key.startsWith(prefix + ".")
0522: || key.equals(prefix);
0523: }
0524: });
0525: }
0526:
0527: /**
0528: * {@inheritDoc}
0529: */
0530: public Properties getProperties(String key) {
0531: return getProperties(key, null);
0532: }
0533:
0534: /**
0535: * Get a list of properties associated with the given configuration key.
0536: *
0537: * @param key The configuration key.
0538: * @param defaults Any default values for the returned
0539: * <code>Properties</code> object. Ignored if <code>null</code>.
0540: *
0541: * @return The associated properties if key is found.
0542: *
0543: * @throws ConversionException is thrown if the key maps to an object that
0544: * is not a String/List of Strings.
0545: *
0546: * @throws IllegalArgumentException if one of the tokens is malformed (does
0547: * not contain an equals sign).
0548: */
0549: public Properties getProperties(String key, Properties defaults) {
0550: /*
0551: * Grab an array of the tokens for this key.
0552: */
0553: String[] tokens = getStringArray(key);
0554:
0555: /*
0556: * Each token is of the form 'key=value'.
0557: */
0558: Properties props = defaults == null ? new Properties()
0559: : new Properties(defaults);
0560: for (int i = 0; i < tokens.length; i++) {
0561: String token = tokens[i];
0562: int equalSign = token.indexOf('=');
0563: if (equalSign > 0) {
0564: String pkey = token.substring(0, equalSign).trim();
0565: String pvalue = token.substring(equalSign + 1).trim();
0566: props.put(pkey, pvalue);
0567: } else if (tokens.length == 1 && "".equals(token)) {
0568: // Semantically equivalent to an empty Properties
0569: // object.
0570: break;
0571: } else {
0572: throw new IllegalArgumentException('\'' + token
0573: + "' does not contain an equals sign");
0574: }
0575: }
0576: return props;
0577: }
0578:
0579: /**
0580: * {@inheritDoc}
0581: * @see PropertyConverter#toBoolean(Object)
0582: */
0583: public boolean getBoolean(String key) {
0584: Boolean b = getBoolean(key, null);
0585: if (b != null) {
0586: return b.booleanValue();
0587: } else {
0588: throw new NoSuchElementException('\'' + key
0589: + "' doesn't map to an existing object");
0590: }
0591: }
0592:
0593: /**
0594: * {@inheritDoc}
0595: * @see PropertyConverter#toBoolean(Object)
0596: */
0597: public boolean getBoolean(String key, boolean defaultValue) {
0598: return getBoolean(key,
0599: BooleanUtils.toBooleanObject(defaultValue))
0600: .booleanValue();
0601: }
0602:
0603: /**
0604: * Obtains the value of the specified key and tries to convert it into a
0605: * <code>Boolean</code> object. If the property has no value, the passed
0606: * in default value will be used.
0607: *
0608: * @param key the key of the property
0609: * @param defaultValue the default value
0610: * @return the value of this key converted to a <code>Boolean</code>
0611: * @throws ConversionException if the value cannot be converted to a
0612: * <code>Boolean</code>
0613: * @see PropertyConverter#toBoolean(Object)
0614: */
0615: public Boolean getBoolean(String key, Boolean defaultValue) {
0616: Object value = resolveContainerStore(key);
0617:
0618: if (value == null) {
0619: return defaultValue;
0620: } else {
0621: try {
0622: return PropertyConverter.toBoolean(interpolate(value));
0623: } catch (ConversionException e) {
0624: throw new ConversionException('\'' + key
0625: + "' doesn't map to a Boolean object", e);
0626: }
0627: }
0628: }
0629:
0630: /**
0631: * {@inheritDoc}
0632: */
0633: public byte getByte(String key) {
0634: Byte b = getByte(key, null);
0635: if (b != null) {
0636: return b.byteValue();
0637: } else {
0638: throw new NoSuchElementException('\'' + key
0639: + " doesn't map to an existing object");
0640: }
0641: }
0642:
0643: /**
0644: * {@inheritDoc}
0645: */
0646: public byte getByte(String key, byte defaultValue) {
0647: return getByte(key, new Byte(defaultValue)).byteValue();
0648: }
0649:
0650: /**
0651: * {@inheritDoc}
0652: */
0653: public Byte getByte(String key, Byte defaultValue) {
0654: Object value = resolveContainerStore(key);
0655:
0656: if (value == null) {
0657: return defaultValue;
0658: } else {
0659: try {
0660: return PropertyConverter.toByte(interpolate(value));
0661: } catch (ConversionException e) {
0662: throw new ConversionException('\'' + key
0663: + "' doesn't map to a Byte object", e);
0664: }
0665: }
0666: }
0667:
0668: /**
0669: * {@inheritDoc}
0670: */
0671: public double getDouble(String key) {
0672: Double d = getDouble(key, null);
0673: if (d != null) {
0674: return d.doubleValue();
0675: } else {
0676: throw new NoSuchElementException('\'' + key
0677: + "' doesn't map to an existing object");
0678: }
0679: }
0680:
0681: /**
0682: * {@inheritDoc}
0683: */
0684: public double getDouble(String key, double defaultValue) {
0685: return getDouble(key, new Double(defaultValue)).doubleValue();
0686: }
0687:
0688: /**
0689: * {@inheritDoc}
0690: */
0691: public Double getDouble(String key, Double defaultValue) {
0692: Object value = resolveContainerStore(key);
0693:
0694: if (value == null) {
0695: return defaultValue;
0696: } else {
0697: try {
0698: return PropertyConverter.toDouble(interpolate(value));
0699: } catch (ConversionException e) {
0700: throw new ConversionException('\'' + key
0701: + "' doesn't map to a Double object", e);
0702: }
0703: }
0704: }
0705:
0706: /**
0707: * {@inheritDoc}
0708: */
0709: public float getFloat(String key) {
0710: Float f = getFloat(key, null);
0711: if (f != null) {
0712: return f.floatValue();
0713: } else {
0714: throw new NoSuchElementException('\'' + key
0715: + "' doesn't map to an existing object");
0716: }
0717: }
0718:
0719: /**
0720: * {@inheritDoc}
0721: */
0722: public float getFloat(String key, float defaultValue) {
0723: return getFloat(key, new Float(defaultValue)).floatValue();
0724: }
0725:
0726: /**
0727: * {@inheritDoc}
0728: */
0729: public Float getFloat(String key, Float defaultValue) {
0730: Object value = resolveContainerStore(key);
0731:
0732: if (value == null) {
0733: return defaultValue;
0734: } else {
0735: try {
0736: return PropertyConverter.toFloat(interpolate(value));
0737: } catch (ConversionException e) {
0738: throw new ConversionException('\'' + key
0739: + "' doesn't map to a Float object", e);
0740: }
0741: }
0742: }
0743:
0744: /**
0745: * {@inheritDoc}
0746: */
0747: public int getInt(String key) {
0748: Integer i = getInteger(key, null);
0749: if (i != null) {
0750: return i.intValue();
0751: } else {
0752: throw new NoSuchElementException('\'' + key
0753: + "' doesn't map to an existing object");
0754: }
0755: }
0756:
0757: /**
0758: * {@inheritDoc}
0759: */
0760: public int getInt(String key, int defaultValue) {
0761: Integer i = getInteger(key, null);
0762:
0763: if (i == null) {
0764: return defaultValue;
0765: }
0766:
0767: return i.intValue();
0768: }
0769:
0770: /**
0771: * {@inheritDoc}
0772: */
0773: public Integer getInteger(String key, Integer defaultValue) {
0774: Object value = resolveContainerStore(key);
0775:
0776: if (value == null) {
0777: return defaultValue;
0778: } else {
0779: try {
0780: return PropertyConverter.toInteger(interpolate(value));
0781: } catch (ConversionException e) {
0782: throw new ConversionException('\'' + key
0783: + "' doesn't map to an Integer object", e);
0784: }
0785: }
0786: }
0787:
0788: /**
0789: * {@inheritDoc}
0790: */
0791: public long getLong(String key) {
0792: Long l = getLong(key, null);
0793: if (l != null) {
0794: return l.longValue();
0795: } else {
0796: throw new NoSuchElementException('\'' + key
0797: + "' doesn't map to an existing object");
0798: }
0799: }
0800:
0801: /**
0802: * {@inheritDoc}
0803: */
0804: public long getLong(String key, long defaultValue) {
0805: return getLong(key, new Long(defaultValue)).longValue();
0806: }
0807:
0808: /**
0809: * {@inheritDoc}
0810: */
0811: public Long getLong(String key, Long defaultValue) {
0812: Object value = resolveContainerStore(key);
0813:
0814: if (value == null) {
0815: return defaultValue;
0816: } else {
0817: try {
0818: return PropertyConverter.toLong(interpolate(value));
0819: } catch (ConversionException e) {
0820: throw new ConversionException('\'' + key
0821: + "' doesn't map to a Long object", e);
0822: }
0823: }
0824: }
0825:
0826: /**
0827: * {@inheritDoc}
0828: */
0829: public short getShort(String key) {
0830: Short s = getShort(key, null);
0831: if (s != null) {
0832: return s.shortValue();
0833: } else {
0834: throw new NoSuchElementException('\'' + key
0835: + "' doesn't map to an existing object");
0836: }
0837: }
0838:
0839: /**
0840: * {@inheritDoc}
0841: */
0842: public short getShort(String key, short defaultValue) {
0843: return getShort(key, new Short(defaultValue)).shortValue();
0844: }
0845:
0846: /**
0847: * {@inheritDoc}
0848: */
0849: public Short getShort(String key, Short defaultValue) {
0850: Object value = resolveContainerStore(key);
0851:
0852: if (value == null) {
0853: return defaultValue;
0854: } else {
0855: try {
0856: return PropertyConverter.toShort(interpolate(value));
0857: } catch (ConversionException e) {
0858: throw new ConversionException('\'' + key
0859: + "' doesn't map to a Short object", e);
0860: }
0861: }
0862: }
0863:
0864: /**
0865: * {@inheritDoc}
0866: * @see #setThrowExceptionOnMissing(boolean)
0867: */
0868: public BigDecimal getBigDecimal(String key) {
0869: BigDecimal number = getBigDecimal(key, null);
0870: if (number != null) {
0871: return number;
0872: } else if (isThrowExceptionOnMissing()) {
0873: throw new NoSuchElementException('\'' + key
0874: + "' doesn't map to an existing object");
0875: } else {
0876: return null;
0877: }
0878: }
0879:
0880: /**
0881: * {@inheritDoc}
0882: */
0883: public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
0884: Object value = resolveContainerStore(key);
0885:
0886: if (value == null) {
0887: return defaultValue;
0888: } else {
0889: try {
0890: return PropertyConverter
0891: .toBigDecimal(interpolate(value));
0892: } catch (ConversionException e) {
0893: throw new ConversionException('\'' + key
0894: + "' doesn't map to a BigDecimal object", e);
0895: }
0896: }
0897: }
0898:
0899: /**
0900: * {@inheritDoc}
0901: * @see #setThrowExceptionOnMissing(boolean)
0902: */
0903: public BigInteger getBigInteger(String key) {
0904: BigInteger number = getBigInteger(key, null);
0905: if (number != null) {
0906: return number;
0907: } else if (isThrowExceptionOnMissing()) {
0908: throw new NoSuchElementException('\'' + key
0909: + "' doesn't map to an existing object");
0910: } else {
0911: return null;
0912: }
0913: }
0914:
0915: /**
0916: * {@inheritDoc}
0917: */
0918: public BigInteger getBigInteger(String key, BigInteger defaultValue) {
0919: Object value = resolveContainerStore(key);
0920:
0921: if (value == null) {
0922: return defaultValue;
0923: } else {
0924: try {
0925: return PropertyConverter
0926: .toBigInteger(interpolate(value));
0927: } catch (ConversionException e) {
0928: throw new ConversionException('\'' + key
0929: + "' doesn't map to a BigDecimal object", e);
0930: }
0931: }
0932: }
0933:
0934: /**
0935: * {@inheritDoc}
0936: * @see #setThrowExceptionOnMissing(boolean)
0937: */
0938: public String getString(String key) {
0939: String s = getString(key, null);
0940: if (s != null) {
0941: return s;
0942: } else if (isThrowExceptionOnMissing()) {
0943: throw new NoSuchElementException('\'' + key
0944: + "' doesn't map to an existing object");
0945: } else {
0946: return null;
0947: }
0948: }
0949:
0950: /**
0951: * {@inheritDoc}
0952: */
0953: public String getString(String key, String defaultValue) {
0954: Object value = resolveContainerStore(key);
0955:
0956: if (value instanceof String) {
0957: return interpolate((String) value);
0958: } else if (value == null) {
0959: return interpolate(defaultValue);
0960: } else {
0961: throw new ConversionException('\'' + key
0962: + "' doesn't map to a String object");
0963: }
0964: }
0965:
0966: /**
0967: * Get an array of strings associated with the given configuration key.
0968: * If the key doesn't map to an existing object, an empty array is returned.
0969: * If a property is added to a configuration, it is checked whether it
0970: * contains multiple values. This is obvious if the added object is a list
0971: * or an array. For strings it is checked whether the string contains the
0972: * list delimiter character that can be specified using the
0973: * <code>setListDelimiter()</code> method. If this is the case, the string
0974: * is splitted at these positions resulting in a property with multiple
0975: * values.
0976: *
0977: * @param key The configuration key.
0978: * @return The associated string array if key is found.
0979: *
0980: * @throws ConversionException is thrown if the key maps to an
0981: * object that is not a String/List of Strings.
0982: * @see #setListDelimiter(char)
0983: * @see #setDelimiterParsingDisabled(boolean)
0984: */
0985: public String[] getStringArray(String key) {
0986: Object value = getProperty(key);
0987:
0988: String[] array;
0989:
0990: if (value instanceof String) {
0991: array = new String[1];
0992:
0993: array[0] = interpolate((String) value);
0994: } else if (value instanceof List) {
0995: List list = (List) value;
0996: array = new String[list.size()];
0997:
0998: for (int i = 0; i < array.length; i++) {
0999: array[i] = interpolate((String) list.get(i));
1000: }
1001: } else if (value == null) {
1002: array = new String[0];
1003: } else {
1004: throw new ConversionException('\'' + key
1005: + "' doesn't map to a String/List object");
1006: }
1007: return array;
1008: }
1009:
1010: /**
1011: * {@inheritDoc}
1012: * @see #getStringArray(String)
1013: */
1014: public List getList(String key) {
1015: return getList(key, new ArrayList());
1016: }
1017:
1018: /**
1019: * {@inheritDoc}
1020: */
1021: public List getList(String key, List defaultValue) {
1022: Object value = getProperty(key);
1023: List list;
1024:
1025: if (value instanceof String) {
1026: list = new ArrayList(1);
1027: list.add(interpolate((String) value));
1028: } else if (value instanceof List) {
1029: list = new ArrayList();
1030: List l = (List) value;
1031:
1032: // add the interpolated elements in the new list
1033: Iterator it = l.iterator();
1034: while (it.hasNext()) {
1035: list.add(interpolate(it.next()));
1036: }
1037:
1038: } else if (value == null) {
1039: list = defaultValue;
1040: } else {
1041: throw new ConversionException('\'' + key
1042: + "' doesn't map to a List object: " + value
1043: + ", a " + value.getClass().getName());
1044: }
1045: return list;
1046: }
1047:
1048: /**
1049: * Returns an object from the store described by the key. If the value is a
1050: * List object, replace it with the first object in the list.
1051: *
1052: * @param key The property key.
1053: *
1054: * @return value Value, transparently resolving a possible List dependency.
1055: */
1056: protected Object resolveContainerStore(String key) {
1057: Object value = getProperty(key);
1058: if (value != null) {
1059: if (value instanceof List) {
1060: List list = (List) value;
1061: value = list.isEmpty() ? null : list.get(0);
1062: } else if (value instanceof Object[]) {
1063: Object[] array = (Object[]) value;
1064: value = array.length == 0 ? null : array[0];
1065: } else if (value instanceof boolean[]) {
1066: boolean[] array = (boolean[]) value;
1067: value = array.length == 0 ? null
1068: : array[0] ? Boolean.TRUE : Boolean.FALSE;
1069: } else if (value instanceof byte[]) {
1070: byte[] array = (byte[]) value;
1071: value = array.length == 0 ? null : new Byte(array[0]);
1072: } else if (value instanceof short[]) {
1073: short[] array = (short[]) value;
1074: value = array.length == 0 ? null : new Short(array[0]);
1075: } else if (value instanceof int[]) {
1076: int[] array = (int[]) value;
1077: value = array.length == 0 ? null
1078: : new Integer(array[0]);
1079: } else if (value instanceof long[]) {
1080: long[] array = (long[]) value;
1081: value = array.length == 0 ? null : new Long(array[0]);
1082: } else if (value instanceof float[]) {
1083: float[] array = (float[]) value;
1084: value = array.length == 0 ? null : new Float(array[0]);
1085: } else if (value instanceof double[]) {
1086: double[] array = (double[]) value;
1087: value = array.length == 0 ? null : new Double(array[0]);
1088: }
1089: }
1090:
1091: return value;
1092: }
1093:
1094: }
|