commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
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 GMT
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. <em>Note:</em> 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 <b>null</b> 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<String, ? extends Lookup> 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<String, Lookup> lookups = new HashMap<String, Lookup>();
+        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;
+
 
 /**
  * <p>The main Configuration interface.</p>
@@ -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 <b>null</b>)
+     */
+    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 <b>null</b> 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 <b>null</b>)
+     * @param defLookups a collection with default {@code Lookup} objects (can
+     *        be <b>null</b>)
+     * @see ConfigurationInterpolator
+     */
+    void installInterpolator(Map<String, ? extends Lookup> 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;
+
+/**
+ * <p>
+ * A specialized implementation of the {@code Lookup} interface which uses a
+ * {@code Configuration} object to resolve variables.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ *
+ * @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
+     *        <b>null</b>)
+     * @throws IllegalArgumentException if the configuration is <b>null</b>
+     */
+    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.
  * <pre>
  * &lt;lookups&gt;
- *   &lt;lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/&gt;
+ *   &lt;lookup config-prefix="prefix" config-class="Lookup fully qualified class name"/&gt;
  * &lt;/lookups&gt;
  * </pre>
  * </p>
@@ -415,21 +415,18 @@ public class DefaultConfigurationBuilder
     private static final long serialVersionUID = -3113777854714492123L;
 
     /**
-     * A specialized {@code StrLookup} object which operates on the combined
+     * A specialized {@code Lookup} object which operates on the combined
      * configuration constructed by this builder. This object is used as
      * default lookup for {@code ConfigurationInterpolator} objects assigned to
      * newly created configuration objects.
      */
-    private final StrLookup combinedConfigLookup = new StrLookup()
+    private final Lookup combinedConfigLookup = new Lookup()
     {
-        @Override
-        public String lookup(String key)
+        public Object lookup(String key)
         {
             if (constructedConfiguration != null)
             {
-                Object value =
-                        constructedConfiguration.resolveContainerStore(key);
-                return (value != null) ? value.toString() : null;
+                return constructedConfiguration.resolveContainerStore(key);
             }
             return null;
         }
@@ -657,6 +654,7 @@ public class DefaultConfigurationBuilder
             result.setNodeCombiner(new OverrideCombiner());
         }
 
+        result.getInterpolator().registerLookups(getInterpolator().getLookups());
         return result;
     }
 
@@ -767,9 +765,8 @@ public class DefaultConfigurationBuilder
         {
             XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
             String key = config.getString(KEY_LOOKUP_KEY);
-            StrLookup lookup = (StrLookup) BeanHelper.createBean(decl);
+            Lookup lookup = (Lookup) BeanHelper.createBean(decl);
             BeanHelper.setProperty(lookup, "configuration", this);
-            ConfigurationInterpolator.registerGlobalLookup(key, lookup);
             this.getInterpolator().registerLookup(key, lookup);
         }
     }
@@ -813,7 +810,6 @@ public class DefaultConfigurationBuilder
             EntityResolver resolver = (EntityResolver) BeanHelper.createBean(decl, CatalogResolver.class);
             BeanHelper.setProperty(resolver, "fileSystem", getFileSystem());
             BeanHelper.setProperty(resolver, "baseDir", getBasePath());
-            BeanHelper.setProperty(resolver, "substitutor", getSubstitutor());
             setEntityResolver(resolver);
         }
     }
@@ -1387,7 +1383,7 @@ public class DefaultConfigurationBuilder
                 AbstractConfiguration config)
         {
             ConfigurationInterpolator parent = new ConfigurationInterpolator();
-            parent.setDefaultLookup(decl.getConfigurationBuilder().combinedConfigLookup);
+            parent.addDefaultLookup(decl.getConfigurationBuilder().combinedConfigLookup);
             config.getInterpolator().setParentInterpolator(parent);
         }
     }

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java Tue Dec 25 20:14:56 2012
@@ -35,7 +35,6 @@ import org.apache.commons.configuration.
 import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ExpressionEngine;
 import org.apache.commons.configuration.tree.NodeCombiner;
-import org.apache.commons.lang.text.StrSubstitutor;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -84,7 +83,7 @@ public class DynamicCombinedConfiguratio
     private String loggerName = DynamicCombinedConfiguration.class.getName();
 
     /** The object for handling variable substitution in key patterns. */
-    private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator());
+    private final ConfigurationInterpolator localSubst;
 
     /**
      * Creates a new instance of {@code DynamicCombinedConfiguration} and
@@ -99,6 +98,7 @@ public class DynamicCombinedConfiguratio
         setNodeCombiner(comb);
         setIgnoreReloadExceptions(false);
         setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
+        localSubst = initLocalInterpolator();
     }
 
     /**
@@ -112,6 +112,7 @@ public class DynamicCombinedConfiguratio
         super();
         setIgnoreReloadExceptions(false);
         setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
+        localSubst = initLocalInterpolator();
     }
 
     public void setKeyPattern(String pattern)
@@ -805,7 +806,7 @@ public class DynamicCombinedConfiguratio
 
     private CombinedConfiguration getCurrentConfig()
     {
-        String key = localSubst.replace(keyPattern);
+        String key = String.valueOf(localSubst.interpolate(keyPattern));
         CombinedConfiguration config = configs.get(key);
         // The double-checked works here due to the Thread guarantees of ConcurrentMap.
         if (config == null)
@@ -854,6 +855,19 @@ public class DynamicCombinedConfiguratio
     }
 
     /**
+     * Creates a {@code ConfigurationInterpolator} instance for performing local
+     * variable substitutions.
+     *
+     * @return the {@code ConfigurationInterpolator}
+     */
+    private ConfigurationInterpolator initLocalInterpolator()
+    {
+        ConfigurationInterpolator ci = new ConfigurationInterpolator();
+        ci.registerLookups(getInterpolator().getLookups());
+        return ci;
+    }
+
+    /**
      * Internal class that identifies each Configuration.
      */
     static class ConfigData

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java Tue Dec 25 20:14:56 2012
@@ -41,7 +41,6 @@ import org.apache.commons.configuration.
 import org.apache.commons.configuration.resolver.EntityResolverSupport;
 import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ExpressionEngine;
-import org.apache.commons.lang.text.StrSubstitutor;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.xml.sax.EntityResolver;
@@ -106,7 +105,7 @@ public class MultiFileHierarchicalConfig
     private EntityResolver entityResolver;
 
     /** The internally used helper object for variable substitution. */
-    private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator());
+    private final ConfigurationInterpolator localSubst;
 
     /**
      * Default Constructor.
@@ -116,6 +115,7 @@ public class MultiFileHierarchicalConfig
         super();
         this.init = true;
         setLogger(LogFactory.getLog(loggerName));
+        localSubst = initLocalInterpolator();
     }
 
     /**
@@ -128,6 +128,7 @@ public class MultiFileHierarchicalConfig
         this.pattern = pathPattern;
         this.init = true;
         setLogger(LogFactory.getLog(loggerName));
+        localSubst = initLocalInterpolator();
     }
 
     public void setLoggerName(String name)
@@ -730,7 +731,7 @@ public class MultiFileHierarchicalConfig
      */
     public void removeConfiguration()
     {
-        String path = getSubstitutor().replace(pattern);
+        String path = String.valueOf(getInterpolator().interpolate(pattern));
         configurationsMap.remove(path);
     }
 
@@ -746,7 +747,7 @@ public class MultiFileHierarchicalConfig
         {
             throw new ConfigurationRuntimeException("File pattern must be defined");
         }
-        String path = localSubst.replace(pattern);
+        String path = String.valueOf(localSubst.interpolate(pattern));
 
         if (configurationsMap.containsKey(path))
         {
@@ -848,4 +849,16 @@ public class MultiFileHierarchicalConfig
         }
     }
 
+    /**
+     * Creates a {@code ConfigurationInterpolator} instance for performing local
+     * variable substitutions.
+     *
+     * @return the {@code ConfigurationInterpolator}
+     */
+    private ConfigurationInterpolator initLocalInterpolator()
+    {
+        ConfigurationInterpolator ci = new ConfigurationInterpolator();
+        ci.registerLookups(getInterpolator().getLookups());
+        return ci;
+    }
 }

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java Tue Dec 25 20:14:56 2012
@@ -495,7 +495,7 @@ public class PatternSubtreeConfiguration
     private String makePath()
     {
         String pathPattern = trailing ? path.substring(0, path.length() - 1) : path;
-        return getSubstitutor().replace(pathPattern);
+        return substitute(pathPattern);
     }
 
     /*
@@ -517,6 +517,19 @@ public class PatternSubtreeConfiguration
         {
             pathPattern = path;
         }
-        return getSubstitutor().replace(pathPattern) + item;
+        return substitute(pathPattern) + item;
+    }
+
+    /**
+     * Uses this configuration's {@code ConfigurationInterpolator} to perform
+     * variable substitution on the given pattern string.
+     *
+     * @param pattern the pattern string
+     * @return the string with variables replaced
+     */
+    private String substitute(String pattern)
+    {
+        Object value = getInterpolator().interpolate(pattern);
+        return (value != null) ? value.toString() : null;
     }
 }

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java Tue Dec 25 20:14:56 2012
@@ -38,6 +38,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 
+import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
 import org.apache.commons.lang.BooleanUtils;
 import org.apache.commons.lang.StringUtils;
 
@@ -994,25 +995,20 @@ public final class PropertyConverter
     }
 
     /**
-     * Performs interpolation of the specified value. This method checks if the
-     * given value contains variables of the form <code>${...}</code>. If
-     * this is the case, all occurrences will be substituted by their current
-     * values.
+     * Performs interpolation of the specified value using the given
+     * {@code Configuration} object. This method checks if the given
+     * {@code Configuration} has a {@link ConfigurationInterpolator} object. If
+     * so, it is called to perform interpolation. Otherwise, the passed in value
+     * is return unchanged.
      *
      * @param value the value to be interpolated
      * @param config the current configuration object
      * @return the interpolated value
      */
-    public static Object interpolate(Object value, AbstractConfiguration config)
+    public static Object interpolate(Object value, Configuration config)
     {
-        if (value instanceof String)
-        {
-            return config.getSubstitutor().replace((String) value);
-        }
-        else
-        {
-            return value;
-        }
+        ConfigurationInterpolator interpolator = config.getInterpolator();
+        return (interpolator != null) ? interpolator.interpolate(value) : value;
     }
 
     /**

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java Tue Dec 25 20:14:56 2012
@@ -20,7 +20,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
 import org.apache.commons.configuration.reloading.Reloadable;
 import org.apache.commons.configuration.tree.ConfigurationNode;
 
@@ -152,6 +151,7 @@ public class SubnodeConfiguration extend
         setRootNode(root);
         this.parent = parent;
         initFromParent(parent);
+        initInterpolator();
     }
 
     /**
@@ -350,16 +350,12 @@ public class SubnodeConfiguration extend
     }
 
     /**
-     * Creates a ConfigurationInterpolator with a chain to the parent's
-     * interpolator.
-     *
-     * @return the new interpolator
+     * Initializes the {@code ConfigurationInterpolator} for this sub configuration.
+     * This is a standard {@code ConfigurationInterpolator} which also references
+     * the {@code ConfigurationInterpolator} of the parent configuration.
      */
-    @Override
-    protected ConfigurationInterpolator createInterpolator()
+    private void initInterpolator()
     {
-        ConfigurationInterpolator interpolator = super.createInterpolator();
-        interpolator.setParentInterpolator(getParent().getInterpolator());
-        return interpolator;
+        getInterpolator().setParentInterpolator(getParent().getInterpolator());
     }
 }

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java Tue Dec 25 20:14:56 2012
@@ -19,8 +19,6 @@ package org.apache.commons.configuration
 
 import java.util.Iterator;
 
-import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
-
 /**
  * <p>A subset of another configuration. The new Configuration object contains
  * every key from the parent Configuration that starts with prefix. The prefix
@@ -46,27 +44,35 @@ public class SubsetConfiguration extends
     /**
      * Create a subset of the specified configuration
      *
-     * @param parent The parent configuration
+     * @param parent The parent configuration (must not be <b>null</b>)
      * @param prefix The prefix used to select the properties
+     * @throws IllegalArgumentException if the parent configuration is <b>null</b>
      */
     public SubsetConfiguration(Configuration parent, String prefix)
     {
-        this.parent = parent;
-        this.prefix = prefix;
+        this(parent, prefix, null);
     }
 
     /**
      * Create a subset of the specified configuration
      *
-     * @param parent    The parent configuration
+     * @param parent The parent configuration (must not be <b>null</b>)
      * @param prefix    The prefix used to select the properties
      * @param delimiter The prefix delimiter
+     * @throws IllegalArgumentException if the parent configuration is <b>null</b>
      */
     public SubsetConfiguration(Configuration parent, String prefix, String delimiter)
     {
+        if (parent == null)
+        {
+            throw new IllegalArgumentException(
+                    "Parent configuration must not be null!");
+        }
+
         this.parent = parent;
         this.prefix = prefix;
         this.delimiter = delimiter;
+        initInterpolator();
     }
 
     /**
@@ -192,32 +198,6 @@ public class SubsetConfiguration extends
         return new SubsetIterator(parent.getKeys(prefix));
     }
 
-    @Override
-    protected Object interpolate(Object base)
-    {
-        if (delimiter == null && "".equals(prefix))
-        {
-            return super.interpolate(base);
-        }
-        else
-        {
-            SubsetConfiguration config = new SubsetConfiguration(parent, "");
-            ConfigurationInterpolator interpolator = config.getInterpolator();
-            getInterpolator().registerLocalLookups(interpolator);
-            if (parent instanceof AbstractConfiguration)
-            {
-                interpolator.setParentInterpolator(((AbstractConfiguration) parent).getInterpolator());
-            }
-            return config.interpolate(base);
-        }
-    }
-
-    @Override
-    protected String interpolate(String base)
-    {
-        return super.interpolate(base);
-    }
-
     /**
      * {@inheritDoc}
      *
@@ -328,6 +308,15 @@ public class SubsetConfiguration extends
         }
     }
 
+    /**
+     * Initializes the {@code ConfigurationInterpolator} for this sub configuration.
+     * This is a standard {@code ConfigurationInterpolator} which also references
+     * the {@code ConfigurationInterpolator} of the parent configuration.
+     */
+    private void initInterpolator()
+    {
+        getInterpolator().setParentInterpolator(getParent().getInterpolator());
+    }
 
     /**
      * A specialized iterator to be returned by the {@code getKeys()}

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java Tue Dec 25 20:14:56 2012
@@ -16,11 +16,18 @@
  */
 package org.apache.commons.configuration.interpol;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 
-import org.apache.commons.lang.text.StrLookup;
+import org.apache.commons.lang3.text.StrLookup;
+import org.apache.commons.lang3.text.StrSubstitutor;
 
 /**
  * <p>
@@ -28,140 +35,109 @@ import org.apache.commons.lang.text.StrL
  * objects.
  * </p>
  * <p>
- * 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.
  * </p>
  * <p>
- * {@code ConfigurationInterpolator} works together with the
- * {@code StrSubstitutor} class from <a
- * href="http://commons.apache.org/lang">Commons Lang</a>. 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 <a href="http://commons.apache.org/lang">Commons Lang</a>. Thus it
+ * supports the same syntax of variable expressions.
  * </p>
  * <p>
  * 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
- * <code>${prefix:name}</code>. {@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 <code>${prefix:name}</code>.
+ * {@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-<b>null</b>
+ * variable value is used.
  * </p>
  * <p>
- * 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.)
  * </p>
  * <p>
- * <table border="1">
- * <tr>
- * <th>Prefix</th>
- * <th>Lookup object</th>
- * </tr>
- * <tr>
- * <td valign="top">sys</td>
- * <td>With this prefix a lookup object is associated that is able to resolve
- * system properties.</td>
- * </tr>
- * <tr>
- * <td valign="top">const</td>
- * <td>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
- * <b>static final</b> modifiers). The name of the variable must be of the form
- * {@code <full qualified class name>.<field name>}, e.g.
- * {@code org.apache.commons.configuration.interpol.ConfigurationInterpolator.PREFIX_CONSTANTS}.
- * </td>
- * </tr>
- * </table>
+ * 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.
  * </p>
  * <p>
- * 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.)
- * </p>
- * <p>
- * 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.
  * </p>
  *
  * @version $Id$
  * @since 1.4
  * @author <a
- * href="http://commons.apache.org/configuration/team-list.html">Commons
- * Configuration team</a>
+ *         href="http://commons.apache.org/configuration/team-list.html">Commons
+ *         Configuration team</a>
  */
-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<String, StrLookup> globalLookups;
+    /** A map with the currently registered lookup objects. */
+    private final Map<String, Lookup> prefixLookups;
 
-    /** A map with the locally registered lookup objects. */
-    private Map<String, StrLookup> localLookups;
+    /** Stores the default lookup objects. */
+    private final List<Lookup> 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<String, StrLookup>(globalLookups);
-        }
+        prefixLookups = new ConcurrentHashMap<String, Lookup>();
+        defaultLookups = new CopyOnWriteArrayList<Lookup>();
+        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<String, Lookup> getLookups()
+    {
+        return new HashMap<String, Lookup>(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 <b>null</b>)
-     * @param lookup the lookup object to be used for this prefix (must not be
-     * <b>null</b>)
+     * @param lookup the {@code Lookup} object to be used for this prefix (must
+     *        not be <b>null</b>)
+     * @throws IllegalArgumentException if either the prefix or the
+     *         {@code Lookup} object is <b>null</b>
      */
-    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
+     * <b>null</b>, this method does not have any effect.
+     *
+     * @param lookups the map with lookups to register (may be <b>null</b>)
+     * @throws IllegalArgumentException if the map contains <b>entries</b>
+     */
+    public void registerLookups(Map<String, ? extends Lookup> 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 <b>null</b>)
-     * @param lookup the lookup object to be used for this prefix (must not be
-     * <b>null</b>)
+     * @return a set with the registered variable prefixes
      */
-    public void registerLookup(String prefix, StrLookup lookup)
+    public Set<String> 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<Lookup> getDefaultLookups()
+    {
+        return new ArrayList<Lookup>(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 <b>null</b>)
+     * @throws IllegalArgumentException if the {@code Lookup} object is
+     *         <b>null</b>
+     */
+    public void addDefaultLookup(Lookup defaultLookup)
+    {
+        defaultLookups.add(defaultLookup);
+    }
+
+    /**
+     * Adds all {@code Lookup} objects in the given collection as default
+     * lookups. The collection can be <b>null</b>, then this method has no
+     * effect. It must not contain <b>null</b> entries.
+     *
+     * @param lookups the {@code Lookup} objects to be added as default lookups
+     * @throws IllegalArgumentException if the collection contains a <b>null</b>
+     *         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 <b>null</b>)
      */
-    public Set<String> 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 <b>null</b>)
      */
-    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 <b>null</b>, 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 <b>null</b> 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 <b>null</b> lookup
-     * object will be returned (never <b>null</b>).
-     *
-     * @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 <b>null</b>
-     * @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 <b>null</b>
-     * @since upcoming
-     */
-    public ConfigurationInterpolator getParentInterpolator()
-    {
-        return this.parentInterpolator;
-    }
-
-    // static initializer, sets up the map with the standard lookups
-    static
-    {
-        globalLookups = new HashMap<String, StrLookup>();
-        globalLookups.put(PREFIX_SYSPROPERTIES, StrLookup.systemPropertiesLookup());
-        globalLookups.put(PREFIX_CONSTANTS, new ConstantLookup());
-        globalLookups.put(PREFIX_ENVIRONMENT, new EnvironmentLookup());
+        return new StrSubstitutor(new StrLookup<Object>()
+        {
+            @Override
+            public String lookup(String key)
+            {
+                Object result = resolve(key);
+                return (result != null) ? result.toString() : null;
+            }
+        });
     }
 }

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java Tue Dec 25 20:14:56 2012
@@ -21,7 +21,6 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.commons.lang.ClassUtils;
-import org.apache.commons.lang.text.StrLookup;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -52,13 +51,13 @@ import org.apache.commons.logging.LogFac
  * href="http://commons.apache.org/configuration/team-list.html">Commons
  * Configuration team</a>
  */
-public class ConstantLookup extends StrLookup
+public class ConstantLookup implements Lookup
 {
     /** Constant for the field separator. */
     private static final char FIELD_SEPRATOR = '.';
 
     /** An internally used cache for already retrieved values. */
-    private static Map<String, String> constantCache = new HashMap<String, String>();
+    private static Map<String, Object> constantCache = new HashMap<String, Object>();
 
     /** The logger. */
     private Log log = LogFactory.getLog(getClass());
@@ -75,15 +74,14 @@ public class ConstantLookup extends StrL
      * @return the value of this variable or <b>null</b> if it cannot be
      * resolved
      */
-    @Override
-    public String lookup(String var)
+    public Object lookup(String var)
     {
         if (var == null)
         {
             return null;
         }
 
-        String result;
+        Object result;
         synchronized (constantCache)
         {
             result = constantCache.get(var);
@@ -107,11 +105,11 @@ public class ConstantLookup extends StrL
                 synchronized (constantCache)
                 {
                     // In worst case, the value will be fetched multiple times
-                    // because of this lax synchronisation, but for constant
+                    // because of this lax synchronization, but for constant
                     // values this shouldn't be a problem.
-                    constantCache.put(var, String.valueOf(value));
+                    constantCache.put(var, value);
                 }
-                result = value.toString();
+                result = value;
             }
         }
         catch (Exception ex)

Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java?rev=1425770&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java (added)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java Tue Dec 25 20:14:56 2012
@@ -0,0 +1,87 @@
+/*
+ * 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.interpol;
+
+/**
+ * <p>
+ * An enumeration class defining constants for the {@code Lookup} objects
+ * available for each {@code Configuration} object per default.
+ * </p>
+ * <p>
+ * When a new configuration object derived from {@code AbstractConfiguration} is
+ * created it installs a {@link ConfigurationInterpolator} with a default set of
+ * {@link Lookup} objects. These lookups are defined by this enumeration class.
+ * </p>
+ * <p>
+ * All the default {@code Lookup} classes are state-less, thus their instances
+ * can be shared between multiple configuration objects. Therefore, it makes
+ * sense to keep shared instances in this enumeration class.
+ * </p>
+ *
+ * @version $Id$
+ * @since 2.0
+ */
+public enum DefaultLookups
+{
+    /** The lookup for system properties. */
+    SYSTEM_PROPERTIES("sys", new SystemPropertiesLookup()),
+
+    /** The lookup for environment properties. */
+    ENVIRONMENT("env", new EnvironmentLookup()),
+
+    /** The lookup for constants. */
+    CONST("const", new ConstantLookup());
+
+    /** The prefix under which the associated lookup object is registered. */
+    private final String prefix;
+
+    /** The associated lookup instance. */
+    private final Lookup lookup;
+
+    /**
+     * Creates a new instance of {@code DefaultLookups} and sets the prefix and
+     * the associated lookup instance.
+     *
+     * @param prfx the prefix
+     * @param look the {@code Lookup} instance
+     */
+    private DefaultLookups(String prfx, Lookup look)
+    {
+        prefix = prfx;
+        lookup = look;
+    }
+
+    /**
+     * Returns the standard prefix for the lookup object of this kind.
+     *
+     * @return the prefix
+     */
+    public String getPrefix()
+    {
+        return prefix;
+    }
+
+    /**
+     * Returns the standard {@code Lookup} instance of this kind.
+     *
+     * @return the associated {@code Lookup} object
+     */
+    public Lookup getLookup()
+    {
+        return lookup;
+    }
+}

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java Tue Dec 25 20:14:56 2012
@@ -16,8 +16,6 @@
  */
 package org.apache.commons.configuration.interpol;
 
-import org.apache.commons.configuration.EnvironmentConfiguration;
-import org.apache.commons.lang.text.StrLookup;
 
 /**
  * <p>
@@ -25,7 +23,7 @@ import org.apache.commons.lang.text.StrL
  * variables.
  * </p>
  * <p>
- * This implementation relies on {@link EnvironmentConfiguration} to resolve
+ * This implementation relies on {@link System.getenv()} to resolve
  * environment variables. It can be used for referencing environment variables
  * in configuration files in an easy way, for instance:
  *
@@ -45,21 +43,17 @@ import org.apache.commons.lang.text.StrL
  * @since 1.7
  * @version $Id$
  */
-public class EnvironmentLookup extends StrLookup
+public class EnvironmentLookup implements Lookup
 {
-    /** Stores the underlying {@code EnvironmentConfiguration}. */
-    private final EnvironmentConfiguration environmentConfig = new EnvironmentConfiguration();
-
     /**
      * Performs a lookup for the specified variable. This implementation
-     * directly delegates to a {@code EnvironmentConfiguration}.
+     * directly delegates to a {@code System.getenv()}.
      *
      * @param key the key to lookup
      * @return the value of this key or <b>null</b> if it cannot be resolved
      */
-    @Override
     public String lookup(String key)
     {
-        return environmentConfig.getString(key);
+        return System.getenv(key);
     }
 }

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java Tue Dec 25 20:14:56 2012
@@ -64,7 +64,7 @@ import org.apache.commons.lang.text.StrS
  * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
  * @version $Id$
  */
-public class ExprLookup extends StrLookup
+public class ExprLookup implements Lookup
 {
     /** Prefix to identify a Java Class object */
     private static final String CLASS = "Class:";
@@ -78,6 +78,9 @@ public class ExprLookup extends StrLooku
     /** Configuration being operated on */
     private AbstractConfiguration configuration;
 
+    /** The StrSubstitutor for performing replace operations. */
+    private StrSubstitutor substitutor;
+
     /** The engine. */
     private final JexlEngine engine = new JexlEngine();
 
@@ -166,6 +169,7 @@ public class ExprLookup extends StrLooku
     public void setConfiguration(AbstractConfiguration config)
     {
         this.configuration = config;
+        installSubstitutor(config);
     }
 
     /**
@@ -173,15 +177,14 @@ public class ExprLookup extends StrLooku
      * @param var The expression.
      * @return The String result of the expression.
      */
-    @Override
     public String lookup(String var)
     {
-        ConfigurationInterpolator interp = configuration.getInterpolator();
-        StrSubstitutor subst = new StrSubstitutor(interp, prefixMatcher, suffixMatcher,
-                StrSubstitutor.DEFAULT_ESCAPE);
-
-        String result = subst.replace(var);
+        if(substitutor == null)
+        {
+            return var;
+        }
 
+        String result = substitutor.replace(var);
         try
         {
             Expression exp = engine.createExpression(result);
@@ -196,6 +199,38 @@ public class ExprLookup extends StrLooku
     }
 
     /**
+     * Creates a {@code StrSubstitutor} object which uses the
+     * {@code ConfigurationInterpolator} of the passed in configuration as
+     * lookup object.
+     *
+     * @param config the associated configuration
+     */
+    private void installSubstitutor(AbstractConfiguration config)
+    {
+        final ConfigurationInterpolator interpolator =
+                (config == null) ? null : config.getInterpolator();
+        if (interpolator == null)
+        {
+            substitutor = null;
+        }
+        else
+        {
+            StrLookup variableResolver = new StrLookup()
+            {
+                @Override
+                public String lookup(String key)
+                {
+                    Object value = interpolator.resolve(key);
+                    return (value != null) ? value.toString() : null;
+                }
+            };
+            substitutor =
+                    new StrSubstitutor(variableResolver, prefixMatcher,
+                            suffixMatcher, StrSubstitutor.DEFAULT_ESCAPE);
+        }
+    }
+
+    /**
      * Creates a new {@code JexlContext} and initializes it with the variables
      * managed by this Lookup object.
      *

Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java (original)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java Tue Dec 25 20:14:56 2012
@@ -21,9 +21,10 @@ import static org.junit.Assert.fail;
 
 import java.awt.event.KeyEvent;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
-import org.apache.commons.lang.text.StrLookup;
+import org.apache.commons.configuration.interpol.Lookup;
 
 /**
  * A helper class that defines a bunch of tests related to variable
@@ -63,7 +64,8 @@ public class InterpolationTestHelper
         config.addProperty("path", "/temp,C:\\Temp,/usr/local/tmp");
         config.setProperty("path.current", "${path}");
         assertEquals("Interpolation with multi-valued property",
-                "/temp", config.getString("path.current"));
+                String.valueOf(config.getProperty("path")),
+                config.getString("path.current"));
     }
 
     /**
@@ -164,6 +166,26 @@ public class InterpolationTestHelper
     }
 
     /**
+     * Tests interpolation of environment properties.
+     *
+     * @param config the configuration to test
+     */
+    public static void testInterpolationEnvironment(Configuration config)
+    {
+        Map<String, String> env = System.getenv();
+        for (Map.Entry<String, String> e : env.entrySet())
+        {
+            config.addProperty("prop" + e.getKey(), "${env:" + e.getKey() + "}");
+        }
+
+        for (Map.Entry<String, String> e : env.entrySet())
+        {
+            assertEquals("Wrong value for environment property " + e.getKey(),
+                    e.getValue(), config.getString("prop" + e.getKey()));
+        }
+    }
+
+    /**
      * Tests interpolation of constant values.
      *
      * @param config the configuration to test
@@ -201,10 +223,9 @@ public class InterpolationTestHelper
     {
         config.addProperty("var", "${echo:testVar}");
         ConfigurationInterpolator interpol = config.getInterpolator();
-        interpol.registerLookup("echo", new StrLookup()
+        interpol.registerLookup("echo", new Lookup()
         {
-            @Override
-            public String lookup(String varName)
+            public Object lookup(String varName)
             {
                 return "Value of variable " + varName;
             }

Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java (original)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java Tue Dec 25 20:14:56 2012
@@ -17,7 +17,6 @@
 package org.apache.commons.configuration;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
@@ -278,21 +277,7 @@ public class TestAbstractConfigurationBa
     {
         AbstractConfiguration config = new TestConfigurationImpl(
                 new PropertiesConfiguration());
-        EnvironmentConfiguration envConfig = new EnvironmentConfiguration();
-        Map<String, Object> env = new HashMap<String, Object>();
-        for (Iterator<String> it = envConfig.getKeys(); it.hasNext();)
-        {
-            String key = it.next();
-            String propKey = "envtest." + key;
-            env.put(propKey, envConfig.getString(key));
-            config.addProperty(propKey, "${env:" + key + "}");
-        }
-        assertFalse("No environment properties", env.isEmpty());
-        for (Map.Entry<String, Object> e : env.entrySet())
-        {
-            assertEquals("Wrong value for " + e.getKey(), e.getValue(), config
-                    .getString(e.getKey()));
-        }
+        InterpolationTestHelper.testInterpolationEnvironment(config);
     }
 
     /**
@@ -360,7 +345,7 @@ public class TestAbstractConfigurationBa
     public void testNestedVariableInterpolation()
     {
         BaseConfiguration config = new BaseConfiguration();
-        config.getSubstitutor().setEnableSubstitutionInVariables(true);
+        config.getInterpolator().setEnableSubstitutionInVariables(true);
         config.addProperty("java.version", "1.4");
         config.addProperty("jre-1.4", "C:\\java\\1.4");
         config.addProperty("jre.path", "${jre-${java.version}}");

Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java Tue Dec 25 20:14:56 2012
@@ -20,15 +20,18 @@ package org.apache.commons.configuration
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Properties;
 import java.util.Set;
@@ -38,6 +41,9 @@ import junitx.framework.ListAssert;
 
 import org.apache.commons.configuration.event.ConfigurationEvent;
 import org.apache.commons.configuration.event.ConfigurationListener;
+import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
+import org.apache.commons.configuration.interpol.Lookup;
+import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -522,6 +528,16 @@ public class TestBaseConfiguration
     }
 
     /**
+     * Tests interpolation of environment properties.
+     */
+    @Test
+    public void testInterpolationEnvironment()
+    {
+        config.setDelimiterParsingDisabled(true);
+        InterpolationTestHelper.testInterpolationEnvironment(config);
+    }
+
+    /**
      * Tests interpolation of constant values.
      */
     @Test
@@ -559,6 +575,60 @@ public class TestBaseConfiguration
         InterpolationTestHelper.testInterpolatedConfiguration(config);
     }
 
+    /**
+     * Tests whether a {@code ConfigurationInterpolator} can be set.
+     */
+    @Test
+    public void testSetInterpolator()
+    {
+        ConfigurationInterpolator interpolator =
+                EasyMock.createMock(ConfigurationInterpolator.class);
+        EasyMock.replay(interpolator);
+        config.setInterpolator(interpolator);
+        assertSame("Interpolator not set", interpolator,
+                config.getInterpolator());
+    }
+
+    /**
+     * Tests whether a {@code ConfigurationInterpolator} can be created and
+     * installed.
+     */
+    @Test
+    public void testInstallInterpolator()
+    {
+        Lookup prefixLookup = EasyMock.createMock(Lookup.class);
+        Lookup defLookup = EasyMock.createMock(Lookup.class);
+        EasyMock.replay(prefixLookup, defLookup);
+        Map<String, Lookup> prefixLookups = new HashMap<String, Lookup>();
+        prefixLookups.put("test", prefixLookup);
+        List<Lookup> defLookups = new ArrayList<Lookup>();
+        defLookups.add(defLookup);
+        config.installInterpolator(prefixLookups, defLookups);
+        ConfigurationInterpolator interpolator = config.getInterpolator();
+        assertEquals("Wrong prefix lookups", prefixLookups,
+                interpolator.getLookups());
+        List<Lookup> defLookups2 = interpolator.getDefaultLookups();
+        assertEquals("Wrong number of default lookups", 2, defLookups2.size());
+        assertSame("Wrong default lookup 1", defLookup, defLookups2.get(0));
+        String var = "testVariable";
+        Object value = 42;
+        config.addProperty(var, value);
+        assertEquals("Wrong lookup result", value,
+                defLookups2.get(1).lookup(var));
+    }
+
+    /**
+     * Tests whether property access is possible without a
+     * {@code ConfigurationInterpolator}.
+     */
+    @Test
+    public void testNoInterpolator()
+    {
+        config.setProperty("test", "${value}");
+        config.setInterpolator(null);
+        assertEquals("Wrong result", "${value}", config.getString("test"));
+    }
+
     @Test
     public void testGetHexadecimalValue()
     {



Mime
View raw message