Return-Path:
X-Original-To: apmail-commons-commits-archive@minotaur.apache.org
Delivered-To: apmail-commons-commits-archive@minotaur.apache.org
Received: from mail.apache.org (hermes.apache.org [140.211.11.3])
by minotaur.apache.org (Postfix) with SMTP id E0C4BE494
for ;
Tue, 25 Dec 2012 20:15:35 +0000 (UTC)
Received: (qmail 4340 invoked by uid 500); 25 Dec 2012 20:15:35 -0000
Delivered-To: apmail-commons-commits-archive@commons.apache.org
Received: (qmail 4291 invoked by uid 500); 25 Dec 2012 20:15:35 -0000
Mailing-List: contact commits-help@commons.apache.org; run by ezmlm
Precedence: bulk
List-Help:
List-Unsubscribe:
List-Post:
List-Id:
Reply-To: dev@commons.apache.org
Delivered-To: mailing list commits@commons.apache.org
Received: (qmail 4284 invoked by uid 99); 25 Dec 2012 20:15:35 -0000
Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230)
by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 25 Dec 2012 20:15:35 +0000
X-ASF-Spam-Status: No, hits=-2000.0 required=5.0
tests=ALL_TRUSTED
X-Spam-Check-By: apache.org
Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4)
by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 25 Dec 2012 20:15:21 +0000
Received: from eris.apache.org (localhost [127.0.0.1])
by eris.apache.org (Postfix) with ESMTP id D0A6623888EA;
Tue, 25 Dec 2012 20:14:58 +0000 (UTC)
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: svn commit: r1425770 [1/2] - in
/commons/proper/configuration/trunk/src:
main/java/org/apache/commons/configuration/
main/java/org/apache/commons/configuration/interpol/
test/java/org/apache/commons/configuration/
test/java/org/apache/commons/configura...
Date: Tue, 25 Dec 2012 20:14:57 -0000
To: commits@commons.apache.org
From: oheger@apache.org
X-Mailer: svnmailer-1.0.8-patched
Message-Id: <20121225201458.D0A6623888EA@eris.apache.org>
X-Virus-Checked: Checked by ClamAV on apache.org
Author: oheger
Date: Tue Dec 25 20:14:56 2012
New Revision: 1425770
URL: http://svn.apache.org/viewvc?rev=1425770&view=rev
Log:
[CONFIGURATION-518] Reworked ConfigurationInterpolator class.
Added:
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java (with props)
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java (with props)
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestConfigurationLookup.java (with props)
Modified:
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java
commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestDefaultConfigurationBuilder.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestDynamicCombinedConfiguration.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestPropertyConverter.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestSubnodeConfiguration.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestSubsetConfiguration.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestVFSConfigurationBuilder.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestWebdavConfigurationBuilder.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestConfigurationInterpolator.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestConstantLookup.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestEnvironmentLookup.java
commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestExprLookup.java
commons/proper/configuration/trunk/src/test/resources/testExpression.xml
Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java Tue Dec 25 20:14:56 2012
@@ -24,8 +24,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
@@ -33,11 +35,11 @@ import org.apache.commons.configuration.
import org.apache.commons.configuration.event.ConfigurationErrorEvent;
import org.apache.commons.configuration.event.ConfigurationErrorListener;
import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
+import org.apache.commons.configuration.interpol.DefaultLookups;
+import org.apache.commons.configuration.interpol.Lookup;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.text.StrLookup;
-import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.NoOpLog;
@@ -142,8 +144,8 @@ public abstract class AbstractConfigurat
*/
private boolean throwExceptionOnMissing;
- /** Stores a reference to the object that handles variable interpolation.*/
- private StrSubstitutor substitutor;
+ /** Stores a reference to the object that handles variable interpolation. */
+ private volatile ConfigurationInterpolator interpolator;
/** Stores the logger.*/
private Log log;
@@ -154,6 +156,7 @@ public abstract class AbstractConfigurat
public AbstractConfiguration()
{
setLogger(null);
+ installDefaultInterpolator();
}
/**
@@ -282,62 +285,63 @@ public abstract class AbstractConfigurat
}
/**
- * Returns the object that is responsible for variable interpolation.
+ * Returns the {@code ConfigurationInterpolator} object that manages the
+ * lookup objects for resolving variables.
*
- * @return the object responsible for variable interpolation
+ * @return the {@code ConfigurationInterpolator} associated with this
+ * configuration
* @since 1.4
*/
- public synchronized StrSubstitutor getSubstitutor()
+ public ConfigurationInterpolator getInterpolator()
{
- if (substitutor == null)
- {
- substitutor = new StrSubstitutor(createInterpolator());
- }
- return substitutor;
+ return interpolator;
}
/**
- * Returns the {@code ConfigurationInterpolator} object that manages
- * the lookup objects for resolving variables. Note: If this
- * object is manipulated (e.g. new lookup objects added), synchronization
- * has to be manually ensured. Because
- * {@code ConfigurationInterpolator} is not thread-safe concurrent
- * access to properties of this configuration instance (which causes the
- * interpolator to be invoked) may cause race conditions.
+ * {@inheritDoc} This implementation sets the passed in object without
+ * further modifications. A null argument is allowed; this disables
+ * interpolation.
*
- * @return the {@code ConfigurationInterpolator} associated with this
- * configuration
- * @since 1.4
+ * @since 2.0
*/
- public ConfigurationInterpolator getInterpolator()
+ public final void setInterpolator(ConfigurationInterpolator ci)
{
- return (ConfigurationInterpolator) getSubstitutor()
- .getVariableResolver();
+ interpolator = ci;
}
/**
- * Creates the interpolator object that is responsible for variable
- * interpolation. This method is invoked on first access of the
- * interpolation features. It creates a new instance of
- * {@code ConfigurationInterpolator} and sets the default lookup
- * object to an implementation that queries this configuration.
+ * {@inheritDoc} This implementation creates a new
+ * {@code ConfigurationInterpolator} instance and initializes it with the
+ * given {@code Lookup} objects. In addition, it adds a specialized default
+ * {@code Lookup} object which queries this {@code Configuration}.
*
- * @return the newly created interpolator object
- * @since 1.4
+ * @since 2.0
*/
- protected ConfigurationInterpolator createInterpolator()
+ public final void installInterpolator(
+ Map prefixLookups,
+ Collection extends Lookup> defLookups)
{
- ConfigurationInterpolator interpol = new ConfigurationInterpolator();
- interpol.setDefaultLookup(new StrLookup()
+ ConfigurationInterpolator ci = new ConfigurationInterpolator();
+ ci.registerLookups(prefixLookups);
+ ci.addDefaultLookups(defLookups);
+ ci.addDefaultLookup(new ConfigurationLookup(this));
+ setInterpolator(ci);
+ }
+
+ /**
+ * Creates a default {@code ConfigurationInterpolator} which is initialized
+ * with all default {@code Lookup} objects. This method is called by the
+ * constructor. It ensures that default interpolation works for every new
+ * configuration instance.
+ */
+ private void installDefaultInterpolator()
+ {
+ Map lookups = new HashMap();
+ for (DefaultLookups l : DefaultLookups.values())
{
- @Override
- public String lookup(String var)
- {
- Object prop = resolveContainerStore(var);
- return (prop != null) ? prop.toString() : null;
- }
- });
- return interpol;
+ lookups.put(l.getPrefix(), l.getLookup());
+ }
+ installInterpolator(lookups, null);
}
/**
@@ -438,37 +442,18 @@ public abstract class AbstractConfigurat
}
/**
- * Returns the interpolated value. Non String values are returned without change.
+ * Returns the interpolated value. This implementation delegates to the
+ * current {@code ConfigurationInterpolator}. If no
+ * {@code ConfigurationInterpolator} is set, the passed in value is returned
+ * without changes.
*
* @param value the value to interpolate
- *
- * @return returns the value with variables substituted
+ * @return the value with variables substituted
*/
protected Object interpolate(Object value)
{
- return PropertyConverter.interpolate(value, this);
- }
-
- /**
- * 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
- * @deprecated Interpolation is now handled by
- * {@link PropertyConverter}; this method will no longer be
- * called
- */
- @Deprecated
- protected String interpolateHelper(String base, List> priorVariables)
- {
- return base; // just a dummy implementation
+ ConfigurationInterpolator ci = getInterpolator();
+ return (ci != null) ? ci.interpolate(value) : value;
}
public Configuration subset(String prefix)
Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java Tue Dec 25 20:14:56 2012
@@ -17,6 +17,12 @@
package org.apache.commons.configuration;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
+import org.apache.commons.configuration.interpol.Lookup;
+
/**
*
The main Configuration interface.
@@ -124,4 +130,43 @@ public interface Configuration extends I
* Remove all properties from the configuration.
*/
void clear();
+
+ /**
+ * Returns the {@code ConfigurationInterpolator} object used by this
+ * {@code Configuration}. This object is responsible for variable
+ * substitution.
+ *
+ * @return the {@code ConfigurationInterpolator} (can be null)
+ */
+ ConfigurationInterpolator getInterpolator();
+
+ /**
+ * Sets the {@code ConfigurationInterpolator} object to be used by this
+ * {@code Configuration}. This object is invoked for each access of a string
+ * property in order to substitute variables which may be contained. The
+ * argument can be null to disable interpolation at all.
+ *
+ * @param ci the new {@code ConfigurationInterpolator}
+ */
+ void setInterpolator(ConfigurationInterpolator ci);
+
+ /**
+ * Creates and installs a new {@code ConfigurationInterpolator} for this
+ * {@code Configuration} based on the passed in arguments. This method
+ * creates a default {@code ConfigurationInterpolator} instance and
+ * initializes it with the passed in {@code Lookup} objects. It also adds a
+ * special default {@code Lookup} object that tries to resolve variables by
+ * matching them with properties contained in this {@code Configuration}.
+ * This is also the main difference to the
+ * {@link #setConfigurationInterpolator(ConfigurationInterpolator)} method
+ * which sets the passed in object as is without adding this special lookup.
+ *
+ * @param prefixLookups the map with {@code Lookup} objects associated with
+ * specific prefixes (can be null)
+ * @param defLookups a collection with default {@code Lookup} objects (can
+ * be null)
+ * @see ConfigurationInterpolator
+ */
+ void installInterpolator(Map prefixLookups,
+ Collection extends Lookup> defLookups);
}
Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java?rev=1425770&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java (added)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java Tue Dec 25 20:14:56 2012
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration;
+
+import org.apache.commons.configuration.interpol.Lookup;
+
+/**
+ *
+ * A specialized implementation of the {@code Lookup} interface which uses a
+ * {@code Configuration} object to resolve variables.
+ *
+ *
+ * This class is passed an {@link ImmutableConfiguration} object at construction
+ * time. In its implementation of the {@code lookup()} method it simply queries
+ * this configuration for the passed in variable name. So the keys passed to
+ * {@code lookup()} are mapped directly to configuration properties.
+ *
+ *
+ * @version $Id$
+ * @since 2.0
+ */
+public class ConfigurationLookup implements Lookup
+{
+ /** The configuration to which lookups are delegated. */
+ private final ImmutableConfiguration configuration;
+
+ /**
+ * Creates a new instance of {@code ConfigurationLookup} and sets the
+ * associated {@code ImmutableConfiguration}.
+ *
+ * @param config the configuration to use for lookups (must not be
+ * null)
+ * @throws IllegalArgumentException if the configuration is null
+ */
+ public ConfigurationLookup(ImmutableConfiguration config)
+ {
+ if (config == null)
+ {
+ throw new IllegalArgumentException(
+ "Configuration must not be null!");
+ }
+ configuration = config;
+ }
+
+ /**
+ * Returns the {@code ImmutableConfiguration} used by this object.
+ *
+ * @return the associated {@code ImmutableConfiguration}
+ */
+ public ImmutableConfiguration getConfiguration()
+ {
+ return configuration;
+ }
+
+ /**
+ * {@inheritDoc} This implementation calls {@code getProperty()} on the
+ * associated configuration. The return value is directly returned. Note
+ * that this may be a complex object, e.g. a collection or an array.
+ */
+ public Object lookup(String variable)
+ {
+ return getConfiguration().getProperty(variable);
+ }
+}
Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java Tue Dec 25 20:14:56 2012
@@ -33,6 +33,7 @@ import org.apache.commons.configuration.
import org.apache.commons.configuration.event.ConfigurationErrorListener;
import org.apache.commons.configuration.event.ConfigurationListener;
import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
+import org.apache.commons.configuration.interpol.Lookup;
import org.apache.commons.configuration.resolver.CatalogResolver;
import org.apache.commons.configuration.resolver.EntityRegistry;
import org.apache.commons.configuration.resolver.EntityResolverSupport;
@@ -40,7 +41,6 @@ import org.apache.commons.configuration.
import org.apache.commons.configuration.tree.DefaultExpressionEngine;
import org.apache.commons.configuration.tree.OverrideCombiner;
import org.apache.commons.configuration.tree.UnionCombiner;
-import org.apache.commons.lang.text.StrLookup;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.EntityResolver;
@@ -189,7 +189,7 @@ import org.xml.sax.EntityResolver;
* section.
*
- * Each instance of {@code AbstractConfiguration} is associated with an
- * object of this class. All interpolation tasks are delegated to this object.
+ * Each instance of {@code AbstractConfiguration} is associated with an object
+ * of this class. All interpolation tasks are delegated to this object.
*
*
- * {@code ConfigurationInterpolator} works together with the
- * {@code StrSubstitutor} class from Commons Lang. By extending
- * {@code StrLookup} it is able to provide values for variables that
- * appear in expressions.
+ * {@code ConfigurationInterpolator} internally uses the {@code StrSubstitutor}
+ * class from Commons Lang. Thus it
+ * supports the same syntax of variable expressions.
*
*
* The basic idea of this class is that it can maintain a set of primitive
- * {@code StrLookup} objects, each of which is identified by a special
- * prefix. The variables to be processed have the form
- * ${prefix:name}. {@code ConfigurationInterpolator} will
- * extract the prefix and determine, which primitive lookup object is registered
- * for it. Then the name of the variable is passed to this object to obtain the
- * actual value. It is also possible to define a default lookup object, which
- * will be used for variables that do not have a prefix or that cannot be
- * resolved by their associated lookup object.
+ * {@link Lookup} objects, each of which is identified by a special prefix. The
+ * variables to be processed have the form ${prefix:name}.
+ * {@code ConfigurationInterpolator} will extract the prefix and determine,
+ * which primitive lookup object is registered for it. Then the name of the
+ * variable is passed to this object to obtain the actual value. It is also
+ * possible to define an arbitrary number of default lookup objects, which are
+ * used for variables that do not have a prefix or that cannot be resolved by
+ * their associated lookup object. When adding default lookup objects their
+ * order matters; they are queried in this order, and the first non-null
+ * variable value is used.
*
*
- * When a new instance of this class is created it is initialized with a default
- * set of primitive lookup objects. This set can be customized using the static
- * methods {@code registerGlobalLookup()} and
- * {@code deregisterGlobalLookup()}. Per default it contains the
- * following standard lookup objects:
+ * After an instance has been created it does not contain any {@code Lookup}
+ * objects. The current set of lookup objects can be modified using the
+ * {@code registerLookup()} and {@code deregisterLookup()} methods. Default
+ * lookup objects (that are invoked for variables without a prefix) can be added
+ * or removed with the {@code addDefaultLookup()} and
+ * {@code removeDefaultLookup()} methods respectively. (When a
+ * {@code ConfigurationInterpolator} instance is created by a configuration
+ * object, a default lookup object is added pointing to the configuration
+ * itself, so that variables are resolved using the configuration's properties.)
*
*
- *
- *
- *
Prefix
- *
Lookup object
- *
- *
- *
sys
- *
With this prefix a lookup object is associated that is able to resolve
- * system properties.
- *
- *
- *
const
- *
The {@code const} prefix indicates that a variable is to be
- * interpreted as a constant member field of a class (i.e. a field with the
- * static final modifiers). The name of the variable must be of the form
- * {@code .}, e.g.
- * {@code org.apache.commons.configuration.interpol.ConfigurationInterpolator.PREFIX_CONSTANTS}.
- *
- *
- *
+ * The default usage scenario is that on a fully initialized instance the
+ * {@code interpolate()} method is called. It is passed an object value which
+ * may contain variables. All these variables are substituted if they can be
+ * resolved. The result is the passed in value with variables replaced.
+ * Alternatively, the {@code resolve()} method can be called to obtain the
+ * values of specific variables without performing interpolation.
*
*
- * After an instance has been created the current set of lookup objects can be
- * modified using the {@code registerLookup()} and
- * {@code deregisterLookup()} methods. The default lookup object (that is
- * invoked for variables without a prefix) can be set with the
- * {@code setDefaultLookup()} method. (If a
- * {@code ConfigurationInterpolator} instance is created by a
- * configuration object, this lookup points to the configuration itself, so that
- * variables are resolved using the configuration's properties. This ensures
- * backward compatibility to earlier version of Commons Configuration.)
- *
- *
- * Implementation node: Instances of this class are not thread-safe related to
- * modifications of their current set of registered lookup objects. It is
- * intended that each instance is associated with a single
- * {@code Configuration} object and used for its interpolation tasks.
+ * Implementation node: This class is thread-safe. Lookup objects can be added
+ * or removed at any time concurrent to interpolation operations.
*
*
* @version $Id$
* @since 1.4
* @author Commons
- * Configuration team
+ * href="http://commons.apache.org/configuration/team-list.html">Commons
+ * Configuration team
*/
-public class ConfigurationInterpolator extends StrLookup
+public class ConfigurationInterpolator
{
- /**
- * Constant for the prefix of the standard lookup object for resolving
- * system properties.
- */
- public static final String PREFIX_SYSPROPERTIES = "sys";
-
- /**
- * Constant for the prefix of the standard lookup object for resolving
- * constant values.
- */
- public static final String PREFIX_CONSTANTS = "const";
-
- /**
- * Constant for the prefix of the standard lookup object for resolving
- * environment properties.
- * @since 1.7
- */
- public static final String PREFIX_ENVIRONMENT = "env";
-
/** Constant for the prefix separator. */
private static final char PREFIX_SEPARATOR = ':';
- /** A map with the globally registered lookup objects. */
- private static Map globalLookups;
+ /** A map with the currently registered lookup objects. */
+ private final Map prefixLookups;
- /** A map with the locally registered lookup objects. */
- private Map localLookups;
+ /** Stores the default lookup objects. */
+ private final List defaultLookups;
- /** Stores the default lookup object. */
- private StrLookup defaultLookup;
+ /** The helper object performing variable substitution. */
+ private final StrSubstitutor substitutor;
/** Stores a parent interpolator objects if the interpolator is nested hierarchically. */
- private ConfigurationInterpolator parentInterpolator;
+ private volatile ConfigurationInterpolator parentInterpolator;
/**
* Creates a new instance of {@code ConfigurationInterpolator}.
*/
public ConfigurationInterpolator()
{
- synchronized (globalLookups)
- {
- localLookups = new HashMap(globalLookups);
- }
+ prefixLookups = new ConcurrentHashMap();
+ defaultLookups = new CopyOnWriteArrayList();
+ substitutor = initSubstitutor();
}
/**
- * Registers the given lookup object for the specified prefix globally. This
- * means that all instances that are created later will use this lookup
- * object for this prefix. If for this prefix a lookup object is already
- * registered, the new lookup object will replace the old one. Note that the
- * lookup objects registered here will be shared between multiple clients.
- * So they should be thread-safe.
+ * Returns a map with the currently registered {@code Lookup} objects and
+ * their prefixes. This is a snapshot copy of the internally used map. So
+ * modifications of this map do not effect this instance.
+ *
+ * @return a copy of the map with the currently registered {@code Lookup}
+ * objects
+ */
+ public Map getLookups()
+ {
+ return new HashMap(prefixLookups);
+ }
+
+ /**
+ * Registers the given {@code Lookup} object for the specified prefix at
+ * this instance. From now on this lookup object will be used for variables
+ * that have the specified prefix.
*
* @param prefix the variable prefix (must not be null)
- * @param lookup the lookup object to be used for this prefix (must not be
- * null)
+ * @param lookup the {@code Lookup} object to be used for this prefix (must
+ * not be null)
+ * @throws IllegalArgumentException if either the prefix or the
+ * {@code Lookup} object is null
*/
- public static void registerGlobalLookup(String prefix, StrLookup lookup)
+ public void registerLookup(String prefix, Lookup lookup)
{
if (prefix == null)
{
@@ -173,115 +149,191 @@ public class ConfigurationInterpolator e
throw new IllegalArgumentException(
"Lookup object must not be null!");
}
- synchronized (globalLookups)
+ prefixLookups.put(prefix, lookup);
+ }
+
+ /**
+ * Registers all {@code Lookup} objects in the given map with their prefixes
+ * at this {@code ConfigurationInterpolator}. Using this method multiple
+ * {@code Lookup} objects can be registered at once. If the passed in map is
+ * null, this method does not have any effect.
+ *
+ * @param lookups the map with lookups to register (may be null)
+ * @throws IllegalArgumentException if the map contains entries
+ */
+ public void registerLookups(Map lookups)
+ {
+ if (lookups != null)
{
- globalLookups.put(prefix, lookup);
+ prefixLookups.putAll(lookups);
}
}
/**
- * Deregisters the global lookup object for the specified prefix. This means
- * that this lookup object won't be available for later created instances
- * any more. For already existing instances this operation does not have any
- * impact.
+ * Deregisters the {@code Lookup} object for the specified prefix at this
+ * instance. It will be removed from this instance.
*
* @param prefix the variable prefix
* @return a flag whether for this prefix a lookup object had been
- * registered
+ * registered
*/
- public static boolean deregisterGlobalLookup(String prefix)
+ public boolean deregisterLookup(String prefix)
{
- synchronized (globalLookups)
- {
- return globalLookups.remove(prefix) != null;
- }
+ return prefixLookups.remove(prefix) != null;
}
/**
- * Registers the given lookup object for the specified prefix at this
- * instance. From now on this lookup object will be used for variables that
- * have the specified prefix.
+ * Returns an unmodifiable set with the prefixes, for which {@code Lookup}
+ * objects are registered at this instance. This means that variables with
+ * these prefixes can be processed.
*
- * @param prefix the variable prefix (must not be null)
- * @param lookup the lookup object to be used for this prefix (must not be
- * null)
+ * @return a set with the registered variable prefixes
*/
- public void registerLookup(String prefix, StrLookup lookup)
+ public Set prefixSet()
{
- if (prefix == null)
- {
- throw new IllegalArgumentException(
- "Prefix for lookup object must not be null!");
- }
- if (lookup == null)
+ return Collections.unmodifiableSet(prefixLookups.keySet());
+ }
+
+ /**
+ * Returns a collection with the default {@code Lookup} objects
+ * added to this {@code ConfigurationInterpolator}. These objects are not
+ * associated with a variable prefix. The returned list is a snapshot copy
+ * of the internal collection of default lookups; so manipulating it does
+ * not affect this instance.
+ *
+ * @return the default lookup objects
+ */
+ public List getDefaultLookups()
+ {
+ return new ArrayList(defaultLookups);
+ }
+
+ /**
+ * Adds a default {@code Lookup} object. Default {@code Lookup} objects are
+ * queried (in the order they were added) for all variables without a
+ * special prefix. If no default {@code Lookup} objects are present, such
+ * variables won't be processed.
+ *
+ * @param defaultLookup the default {@code Lookup} object to be added (must
+ * not be null)
+ * @throws IllegalArgumentException if the {@code Lookup} object is
+ * null
+ */
+ public void addDefaultLookup(Lookup defaultLookup)
+ {
+ defaultLookups.add(defaultLookup);
+ }
+
+ /**
+ * Adds all {@code Lookup} objects in the given collection as default
+ * lookups. The collection can be null, then this method has no
+ * effect. It must not contain null entries.
+ *
+ * @param lookups the {@code Lookup} objects to be added as default lookups
+ * @throws IllegalArgumentException if the collection contains a null
+ * entry
+ */
+ public void addDefaultLookups(Collection extends Lookup> lookups)
+ {
+ if (lookups != null)
{
- throw new IllegalArgumentException(
- "Lookup object must not be null!");
+ defaultLookups.addAll(lookups);
}
- localLookups.put(prefix, lookup);
}
/**
- * Deregisters the lookup object for the specified prefix at this instance.
- * It will be removed from this instance.
+ * Removes the specified {@code Lookup} object from the list of default
+ * {@code Lookup}s.
*
- * @param prefix the variable prefix
- * @return a flag whether for this prefix a lookup object had been
- * registered
+ * @param lookup the {@code Lookup} object to be removed
+ * @return a flag whether this {@code Lookup} object actually existed and
+ * was removed
*/
- public boolean deregisterLookup(String prefix)
+ public boolean removeDefaultLookup(Lookup lookup)
{
- return localLookups.remove(prefix) != null;
+ return defaultLookups.remove(lookup);
}
/**
- * Returns a set with the prefixes, for which lookup objects are registered
- * at this instance. This means that variables with these prefixes can be
- * processed.
+ * Sets the parent {@code ConfigurationInterpolator}. This object is used if
+ * the {@code Lookup} objects registered at this object cannot resolve a
+ * variable.
*
- * @return a set with the registered variable prefixes
+ * @param parentInterpolator the parent {@code ConfigurationInterpolator}
+ * object (can be null)
*/
- public Set prefixSet()
+ public void setParentInterpolator(
+ ConfigurationInterpolator parentInterpolator)
{
- return localLookups.keySet();
+ this.parentInterpolator = parentInterpolator;
}
/**
- * Returns the default lookup object.
+ * Returns the parent {@code ConfigurationInterpolator}.
*
- * @return the default lookup object
+ * @return the parent {@code ConfigurationInterpolator} (can be null)
*/
- public StrLookup getDefaultLookup()
+ public ConfigurationInterpolator getParentInterpolator()
{
- return defaultLookup;
+ return this.parentInterpolator;
}
/**
- * Sets the default lookup object. This lookup object will be used for all
- * variables without a special prefix. If it is set to null, such
- * variables won't be processed.
+ * Sets a flag that variable names can contain other variables. If enabled,
+ * variable substitution is also done in variable names.
+ *
+ * @return the substitution in variables flag
+ */
+ public boolean isEnableSubstitutionInVariables()
+ {
+ return substitutor.isEnableSubstitutionInVariables();
+ }
+
+ /**
+ * Sets the flag whether variable names can contain other variables. This
+ * flag corresponds to the {@code enableSubstitutionInVariables} property of
+ * the underlying {@code StrSubstitutor} object.
*
- * @param defaultLookup the new default lookup object
+ * @param f the new value of the flag
*/
- public void setDefaultLookup(StrLookup defaultLookup)
+ public void setEnableSubstitutionInVariables(boolean f)
{
- this.defaultLookup = defaultLookup;
+ substitutor.setEnableSubstitutionInVariables(f);
}
/**
- * Resolves the specified variable. This implementation will try to extract
+ * Performs interpolation of the passed in value. If the value is of type
+ * String, this method checks whether it contains variables. If so, all
+ * variables are replaced by their current values (if possible). For non
+ * string arguments, the value is returned without changes.
+ *
+ * @param value the value to be interpolated
+ * @return the interpolated value
+ */
+ public Object interpolate(Object value)
+ {
+ if (value instanceof String)
+ {
+ return substitutor.replace((String) value);
+ }
+ return value;
+ }
+
+ /**
+ * Resolves the specified variable. This implementation tries to extract
* a variable prefix from the given variable name (the first colon (':') is
* used as prefix separator). It then passes the name of the variable with
* the prefix stripped to the lookup object registered for this prefix. If
* no prefix can be found or if the associated lookup object cannot resolve
- * this variable, the default lookup object will be used.
+ * this variable, the default lookup objects are used. If this is not
+ * successful either and a parent {@code ConfigurationInterpolator} is
+ * available, this object is asked to resolve the variable.
*
* @param var the name of the variable whose value is to be looked up
* @return the value of this variable or null if it cannot be
* resolved
*/
- @Override
- public String lookup(String var)
+ public Object resolve(String var)
{
if (var == null)
{
@@ -293,35 +345,28 @@ public class ConfigurationInterpolator e
{
String prefix = var.substring(0, prefixPos);
String name = var.substring(prefixPos + 1);
- String value = fetchLookupForPrefix(prefix).lookup(name);
- if (value == null && getParentInterpolator() != null)
+ Object value = fetchLookupForPrefix(prefix).lookup(name);
+ if (value != null)
{
- value = getParentInterpolator().lookup(name);
+ return value;
}
+ }
+
+ for (Lookup l : defaultLookups)
+ {
+ Object value = l.lookup(var);
if (value != null)
{
return value;
}
}
- String value = fetchNoPrefixLookup().lookup(var);
- if (value == null && getParentInterpolator() != null)
+
+ ConfigurationInterpolator parent = getParentInterpolator();
+ if (parent != null)
{
- value = getParentInterpolator().lookup(var);
+ return getParentInterpolator().resolve(var);
}
- return value;
- }
-
- /**
- * Returns the lookup object to be used for variables without a prefix. This
- * implementation will check whether a default lookup object was set. If
- * this is the case, it will be returned. Otherwise a null lookup
- * object will be returned (never null).
- *
- * @return the lookup object to be used for variables without a prefix
- */
- protected StrLookup fetchNoPrefixLookup()
- {
- return (getDefaultLookup() != null) ? getDefaultLookup() : StrLookup.noneLookup();
+ return null;
}
/**
@@ -333,57 +378,34 @@ public class ConfigurationInterpolator e
* @param prefix the prefix
* @return the lookup object to be used for this prefix
*/
- protected StrLookup fetchLookupForPrefix(String prefix)
+ protected Lookup fetchLookupForPrefix(String prefix)
{
- StrLookup lookup = localLookups.get(prefix);
+ Lookup lookup = prefixLookups.get(prefix);
if (lookup == null)
{
- lookup = StrLookup.noneLookup();
+ lookup = DummyLookup.INSTANCE;
}
return lookup;
}
/**
- * Registers the local lookup instances for the given interpolator.
+ * Creates and initializes a {@code StrSubstitutor} object which is used for
+ * variable substitution. This {@code StrSubstitutor} is assigned a
+ * specialized lookup object implementing the correct variable resolving
+ * algorithm.
*
- * @param interpolator the instance receiving the local lookups
- * @since upcoming
+ * @return the {@code StrSubstitutor} used by this object
*/
- public void registerLocalLookups(ConfigurationInterpolator interpolator)
+ private StrSubstitutor initSubstitutor()
{
- interpolator.localLookups.putAll(localLookups);
- }
-
- /**
- * Sets the parent interpolator. This object is used if the interpolation is nested
- * hierarchically and the current interpolation object cannot resolve a variable.
- *
- * @param parentInterpolator the parent interpolator object or null
- * @since upcoming
- */
- public void setParentInterpolator(ConfigurationInterpolator parentInterpolator)
- {
- this.parentInterpolator = parentInterpolator;
- }
-
- /**
- * Requests the parent interpolator. This object is used if the interpolation is nested
- * hierarchically and the current interpolation
- *
- * @return the parent interpolator or null
- * @since upcoming
- */
- public ConfigurationInterpolator getParentInterpolator()
- {
- return this.parentInterpolator;
- }
-
- // static initializer, sets up the map with the standard lookups
- static
- {
- globalLookups = new HashMap();
- globalLookups.put(PREFIX_SYSPROPERTIES, StrLookup.systemPropertiesLookup());
- globalLookups.put(PREFIX_CONSTANTS, new ConstantLookup());
- globalLookups.put(PREFIX_ENVIRONMENT, new EnvironmentLookup());
+ return new StrSubstitutor(new StrLookup