You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by rg...@apache.org on 2009/04/11 08:23:15 UTC

svn commit: r764168 - in /commons/proper/configuration/trunk: ./ conf/ conf/01/ src/java/org/apache/commons/configuration/ src/java/org/apache/commons/configuration/beanutils/ src/java/org/apache/commons/configuration/interpol/ src/java/org/apache/comm...

Author: rgoers
Date: Sat Apr 11 06:23:13 2009
New Revision: 764168

URL: http://svn.apache.org/viewvc?rev=764168&view=rev
Log:
CONFIGURATION-380 - Add expression evaluation during interpolation. Fix various bugs in VFS reloading and in multi file handling

Added:
    commons/proper/configuration/trunk/conf/01/
    commons/proper/configuration/trunk/conf/01/testMultiConfiguration_1001.xml
      - copied unchanged from r760159, commons/proper/configuration/trunk/conf/testMultiConfiguration_1001.xml
    commons/proper/configuration/trunk/conf/testExpression.xml
      - copied, changed from r760159, commons/proper/configuration/trunk/conf/testExtendedClass.xml
    commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder.xml
      - copied, changed from r760159, commons/proper/configuration/trunk/conf/testMultiTenentConfigurationBuilder.xml
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/interpol/ExprLookup.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/TreeUtils.java
    commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/interpol/TestExprLookup.java
Modified:
    commons/proper/configuration/trunk/conf/testMultiTenentConfigurationBuilder.xml
    commons/proper/configuration/trunk/pom.xml
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/VFSFileSystem.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/beanutils/BeanHelper.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/MergeCombiner.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java
    commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDefaultConfigurationBuilder.java
    commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java
    commons/proper/configuration/trunk/xdocs/changes.xml
    commons/proper/configuration/trunk/xdocs/userguide/howto_basicfeatures.xml

Copied: commons/proper/configuration/trunk/conf/testExpression.xml (from r760159, commons/proper/configuration/trunk/conf/testExtendedClass.xml)
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/conf/testExpression.xml?p2=commons/proper/configuration/trunk/conf/testExpression.xml&p1=commons/proper/configuration/trunk/conf/testExtendedClass.xml&r1=760159&r2=764168&rev=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/conf/testExtendedClass.xml (original)
+++ commons/proper/configuration/trunk/conf/testExpression.xml Sat Apr 11 06:23:13 2009
@@ -2,33 +2,37 @@
 <!-- Test configuration definition file that demonstrates complex initialization -->
 <configuration>
   <header>
-    <result delimiterParsingDisabled="true"
-            config-class="org.apache.commons.configuration.TestDefaultConfigurationBuilder$ExtendedCombinedConfiguration">
-      <nodeCombiner config-class="org.apache.commons.configuration.tree.OverrideCombiner"/>
-      <expressionEngine config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
+    <result delimiterParsingDisabled="true" forceReloadCheck="true"
+            config-class="org.apache.commons.configuration.DynamicCombinedConfiguration"
+            keyPattern="$${sys:Id}">
+      <nodeCombiner config-class="org.apache.commons.configuration.tree.MergeCombiner"/>
+      <expressionEngine
+          config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
     </result>
-    <combiner>
-      <override>
-        <list-nodes>
-          <node>table</node>
-          <node>list</node>
-        </list-nodes>
-      </override>
-    </combiner>
+    <lookups>
+      <lookup config-prefix="expr" 
+              config-class="org.apache.commons.configuration.interpol.ExprLookup">
+        <variables>
+          <variable name="String" value="Class:org.apache.commons.lang.StringUtils"/>
+        </variables>
+      </lookup>
+    </lookups>
+    <providers>
+      <provider config-tag="multifile"
+         config-class="org.apache.commons.configuration.DefaultConfigurationBuilder$FileConfigurationProvider"
+         configurationClass="org.apache.commons.configuration.MultiFileHierarchicalConfiguration"/>
+    </providers>
   </header>
-  <system/>
-  <properties fileName="test.properties" throwExceptionOnMissing="true"
-    config-name="properties">
-    <reloadingStrategy config-class="org.apache.commons.configuration.reloading.FileChangedReloadingStrategy"
-      refreshDelay="10000"/>
-  </properties>
-  <!-- Fetch the file name from a variable -->
-  <xml fileName="${test_file_xml}" config-name="xml">
-    <expressionEngine config-class="org.apache.commons.configuration.tree.DefaultExpressionEngine"
-      propertyDelimiter="/" indexStart="[" indexEnd="]"/>
-  </xml>
-  <additional>
-    <xml config-name="combiner1" fileName="${test_file_combine}"/>  -->
-    <xml config-name="combiner2" fileName="testcombine2.xml"/>
-  </additional>
+  <override>
+    <multifile filePattern='$$${expr:String.right("$[sys:Id]", 2)}/testMultiConfiguration_$$${sys:Id}.xml'
+               config-name="clientConfig" delimiterParsingDisabled="true">
+       <expressionEngine
+          config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
+    </multifile>
+    <xml fileName="testMultiConfiguration_default.xml"
+         config-name="defaultConfig" delimiterParsingDisabled="true">
+      <expressionEngine
+          config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
+    </xml>
+  </override>
 </configuration>
\ No newline at end of file

Copied: commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder.xml (from r760159, commons/proper/configuration/trunk/conf/testMultiTenentConfigurationBuilder.xml)
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder.xml?p2=commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder.xml&p1=commons/proper/configuration/trunk/conf/testMultiTenentConfigurationBuilder.xml&r1=760159&r2=764168&rev=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/conf/testMultiTenentConfigurationBuilder.xml (original)
+++ commons/proper/configuration/trunk/conf/testFileMonitorConfigurationBuilder.xml Sat Apr 11 06:23:13 2009
@@ -9,6 +9,7 @@
       <expressionEngine
           config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
     </result>
+    <fileSystem config-class="org.apache.commons.configuration.VFSFileSystem"/>
     <providers>
       <provider config-tag="multifile"
          config-class="org.apache.commons.configuration.DefaultConfigurationBuilder$FileConfigurationProvider"
@@ -16,15 +17,19 @@
     </providers>
   </header>
   <override>
-    <multifile filePattern="testMultiConfiguration_$$${sys:Id}.xml"
-               config-name="clientConfig" delimiterParsingDisabled="true">
+    <multifile filePattern="testwrite/testMultiConfiguration_$$${sys:Id}.xml"
+               config-name="clientConfig" delimiterParsingDisabled="true" schemaValidation="false">
        <expressionEngine
           config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
+       <reloadingStrategy delay="500"
+          config-class="org.apache.commons.configuration.reloading.VFSFileMonitorReloadingStrategy"/>
     </multifile>
     <xml fileName="testMultiConfiguration_default.xml"
          config-name="defaultConfig" delimiterParsingDisabled="true">
       <expressionEngine
           config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
+      <reloadingStrategy
+          config-class="org.apache.commons.configuration.reloading.VFSFileMonitorReloadingStrategy"/>
     </xml>
   </override>
 </configuration>
\ No newline at end of file

Modified: commons/proper/configuration/trunk/conf/testMultiTenentConfigurationBuilder.xml
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/conf/testMultiTenentConfigurationBuilder.xml?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/conf/testMultiTenentConfigurationBuilder.xml (original)
+++ commons/proper/configuration/trunk/conf/testMultiTenentConfigurationBuilder.xml Sat Apr 11 06:23:13 2009
@@ -2,7 +2,7 @@
 <!-- Test configuration definition file that demonstrates complex initialization -->
 <configuration>
   <header>
-    <result delimiterParsingDisabled="true" forceReloadCheck="true"
+    <result delimiterParsingDisabled="true" forceReloadCheck="true" loggerName="TestLogger"
             config-class="org.apache.commons.configuration.DynamicCombinedConfiguration"
             keyPattern="$${sys:Id}">
       <nodeCombiner config-class="org.apache.commons.configuration.tree.MergeCombiner"/>
@@ -17,7 +17,7 @@
   </header>
   <override>
     <multifile filePattern="testMultiConfiguration_$$${sys:Id}.xml"
-               config-name="clientConfig" delimiterParsingDisabled="true">
+               config-name="clientConfig" delimiterParsingDisabled="true" schemaValidation="false">
        <expressionEngine
           config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine"/>
     </multifile>

Modified: commons/proper/configuration/trunk/pom.xml
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/pom.xml?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/pom.xml (original)
+++ commons/proper/configuration/trunk/pom.xml Sat Apr 11 06:23:13 2009
@@ -250,6 +250,13 @@
     </dependency>
 
     <dependency>
+      <groupId>commons-jexl</groupId>
+      <artifactId>commons-jexl</artifactId>
+      <version>1.1</version>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-vfs</artifactId>
       <version>2.0-SNAPSHOT</version>

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/CombinedConfiguration.java Sat Apr 11 06:23:13 2009
@@ -23,6 +23,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.io.PrintStream;
+import java.io.ByteArrayOutputStream;
 
 import org.apache.commons.configuration.event.ConfigurationEvent;
 import org.apache.commons.configuration.event.ConfigurationListener;
@@ -34,6 +36,7 @@
 import org.apache.commons.configuration.tree.NodeCombiner;
 import org.apache.commons.configuration.tree.UnionCombiner;
 import org.apache.commons.configuration.tree.ViewNode;
+import org.apache.commons.configuration.tree.TreeUtils;
 
 /**
  * <p>
@@ -737,6 +740,13 @@
                 node = getNodeCombiner().combine(node,
                         ((ConfigData) it.next()).getTransformedRoot());
             }
+            if (getLogger().isDebugEnabled())
+            {
+                ByteArrayOutputStream os = new ByteArrayOutputStream();
+                PrintStream stream = new PrintStream(os);
+                TreeUtils.printTree(stream, node);
+                getLogger().debug(os.toString());
+            }
             return node;
         }
     }

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java Sat Apr 11 06:23:13 2009
@@ -703,7 +703,9 @@
             HierarchicalConfiguration config = (HierarchicalConfiguration) it.next();
             XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
             String key = config.getString(KEY_LOOKUP_KEY);
-            ConfigurationInterpolator.registerGlobalLookup(key, (StrLookup) BeanHelper.createBean(decl));
+            StrLookup lookup = (StrLookup) BeanHelper.createBean(decl);
+            BeanHelper.setProperty(lookup, "configuration", this);
+            ConfigurationInterpolator.registerGlobalLookup(key, lookup);
         }
     }
 

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java Sat Apr 11 06:23:13 2009
@@ -32,6 +32,8 @@
 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.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 /**
  * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be used. Each CombinedConfiguration
@@ -71,6 +73,9 @@
     /** Stores the combiner. */
     private NodeCombiner nodeCombiner;
 
+    /** The name of the logger to use for each CombinedConfiguration */
+    private String loggerName;
+
     /**
      * Creates a new instance of <code>CombinedConfiguration</code> and
      * initializes the combiner to be used.
@@ -106,6 +111,15 @@
     }
 
     /**
+     * Set the name of the Logger to use on each CombinedConfiguration.
+     * @param name The Logger name.
+     */
+    public void setLoggerName(String name)
+    {
+        this.loggerName = name;
+    }
+
+    /**
      * Returns the node combiner that is used for creating the combined node
      * structure.
      *
@@ -746,9 +760,16 @@
             if (config == null)
             {
                 config = new CombinedConfiguration(getNodeCombiner());
+                if (loggerName != null)
+                {
+                    Log log = LogFactory.getLog(loggerName);
+                    if (log != null)
+                    {
+                        config.setLogger(log);
+                    }
+                }
                 config.setExpressionEngine(this.getExpressionEngine());
                 config.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
-                config.setDetailEvents(this.isDetailEvents());
                 config.setConversionExpressionEngine(getConversionExpressionEngine());
                 config.setListDelimiter(getListDelimiter());
                 Iterator iter = config.getErrorListeners().iterator();

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java Sat Apr 11 06:23:13 2009
@@ -37,6 +37,10 @@
 import org.apache.commons.configuration.event.ConfigurationListener;
 import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ExpressionEngine;
+import org.apache.commons.configuration.reloading.ReloadingStrategy;
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 /**
  * This class provides access to multiple configuration files that reside in a location that
@@ -76,6 +80,18 @@
     /** Return an empty configuration if loading fails */
     private boolean ignoreException = true;
 
+    /** Capture the schema validation setting */
+    private boolean schemaValidation;
+
+    /** Stores a flag whether DTD or Schema validation should be performed.*/
+    private boolean validating;
+
+    /** A flag whether attribute splitting is disabled.*/
+    private boolean attributeSplittingDisabled;
+
+    /** The Logger name to use */
+    private String loggerName = "";
+
     /**
      * Default Constructor.
      */
@@ -96,6 +112,11 @@
         this.init = true;
     }
 
+    public void setLoggerName(String name)
+    {
+        this.loggerName = name;
+    }
+
     /**
      * Set the File pattern
      * @param pathPattern The pattern for the path to the configuration.
@@ -105,6 +126,36 @@
         this.pattern = pathPattern;
     }
 
+    public boolean isSchemaValidation()
+    {
+        return schemaValidation;
+    }
+
+    public void setSchemaValidation(boolean schemaValidation)
+    {
+        this.schemaValidation = schemaValidation;
+    }
+
+    public boolean isValidating()
+    {
+        return validating;
+    }
+
+    public void setValidating(boolean validating)
+    {
+        this.validating = validating;
+    }
+
+    public boolean isAttributeSplittingDisabled()
+    {
+        return attributeSplittingDisabled;
+    }
+
+    public void setAttributeSplittingDisabled(boolean attributeSplittingDisabled)
+    {
+        this.attributeSplittingDisabled = attributeSplittingDisabled;
+    }
+
     /**
      * Set to true if an empty Configuration should be returned when loading fails. If
      * false an exception will be thrown.
@@ -620,11 +671,23 @@
         XMLConfiguration configuration = new XMLConfiguration();
         try
         {
+            if (loggerName != null)
+            {
+                Log log = LogFactory.getLog(loggerName);
+                if (log != null)
+                {
+                    configuration.setLogger(log);
+                }
+            }
+            configuration.setBasePath(getBasePath());
             configuration.setFileName(path);
+            configuration.setFileSystem(getFileSystem());
             configuration.setExpressionEngine(getExpressionEngine());
-            configuration.setReloadingStrategy(getReloadingStrategy());
+            configuration.setReloadingStrategy(createReloadingStrategy());
             configuration.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
-            configuration.setDetailEvents(this.isDetailEvents());
+            configuration.setValidating(validating);
+            configuration.setSchemaValidation(schemaValidation);
+            configuration.setAttributeSplittingDisabled(attributeSplittingDisabled);
             configuration.setListDelimiter(getListDelimiter());
             configuration.addConfigurationListener(this);
             configuration.addErrorListener(this);
@@ -647,4 +710,27 @@
 
         return configuration;
     }
+
+    /**
+     * Clone the FileReloadingStrategy since each file needs its own.
+     * @return A new FileReloadingStrategy.
+     */
+    private ReloadingStrategy createReloadingStrategy()
+    {
+        if (getReloadingStrategy() == null)
+        {
+            return null;
+        }
+        try
+        {
+            ReloadingStrategy strategy = (ReloadingStrategy) BeanUtils.cloneBean(getReloadingStrategy());
+            strategy.setConfiguration(null);
+            return strategy;
+        }
+        catch (Exception ex)
+        {
+            return null;
+        }
+
+    }
 }

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/VFSFileSystem.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/VFSFileSystem.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/VFSFileSystem.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/VFSFileSystem.java Sat Apr 11 06:23:13 2009
@@ -24,6 +24,8 @@
 import org.apache.commons.vfs.FileSystemException;
 import org.apache.commons.vfs.FileType;
 import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.UserAuthenticator;
+import org.apache.commons.vfs.impl.DefaultFileSystemConfigBuilder;
 import org.apache.commons.vfs.provider.UriParser;
 import org.apache.commons.vfs.provider.http.HttpFileSystemConfigBuilder;
 import org.apache.commons.vfs.provider.webdav.WebdavFileSystemConfigBuilder;
@@ -336,10 +338,14 @@
             {
                 return setWebdavOptions(opts, map);
             }
-            if (scheme.equals("http"))
+            else if (scheme.equals("http"))
             {
                 return setHttpOptions(opts, map);
             }
+            else
+            {
+                return setDefaultOptions(opts, map);
+            }
         }
         return opts;
     }
@@ -365,6 +371,8 @@
 
     private FileSystemOptions setHttpOptions(FileSystemOptions opts, Map map)
     {
+        setDefaultOptions(opts, map);
+
         if (httpBuilder == null || map == null)
         {
             return opts;
@@ -390,4 +398,28 @@
         }
         return opts;
     }
+
+    private FileSystemOptions setDefaultOptions(FileSystemOptions opts, Map map)
+    {
+        DefaultFileSystemConfigBuilder builder = DefaultFileSystemConfigBuilder.getInstance();
+
+        if (builder == null || map == null)
+        {
+            return opts;
+        }
+
+        if (map.containsKey("userAuthenticator"))
+        {
+            UserAuthenticator auth = (UserAuthenticator) map.get("userAuthenticator");
+            try
+            {
+                builder.setUserAuthenticator(opts, auth);
+            }
+            catch (FileSystemException e)
+            {
+                return opts;
+            }
+        }
+        return opts;
+    }
 }

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/beanutils/BeanHelper.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/beanutils/BeanHelper.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/beanutils/BeanHelper.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/beanutils/BeanHelper.java Sat Apr 11 06:23:13 2009
@@ -22,6 +22,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.beans.PropertyDescriptor;
 
 import org.apache.commons.beanutils.BeanUtils;
 import org.apache.commons.beanutils.PropertyUtils;
@@ -158,6 +159,32 @@
     public static void initBean(Object bean, BeanDeclaration data)
             throws ConfigurationRuntimeException
     {
+        initBeanProperties(bean, data);
+
+        Map nestedBeans = data.getNestedBeanDeclarations();
+        if (nestedBeans != null)
+        {
+            for (Iterator it = nestedBeans.entrySet().iterator(); it.hasNext();)
+            {
+                Map.Entry e = (Map.Entry) it.next();
+                String propName = (String) e.getKey();
+                Class defaultClass = getDefaultClass(bean, propName);
+                initProperty(bean, propName, createBean(
+                        (BeanDeclaration) e.getValue(), defaultClass));
+            }
+        }
+    }
+
+    /**
+     * Initializes the beans properties.
+     *
+     * @param bean the bean to be initialized
+     * @param data the bean declaration
+     * @throws ConfigurationRuntimeException if a property cannot be set
+     */
+    public static void initBeanProperties(Object bean, BeanDeclaration data)
+            throws ConfigurationRuntimeException
+    {
         Map properties = data.getBeanProperties();
         if (properties != null)
         {
@@ -168,17 +195,28 @@
                 initProperty(bean, propName, e.getValue());
             }
         }
+    }
 
-        Map nestedBeans = data.getNestedBeanDeclarations();
-        if (nestedBeans != null)
+    /**
+     * Return the Class of the property if it can be determined.
+     * @param bean The bean containing the property.
+     * @param propName The name of the property.
+     * @return The class associated with the property or null.
+     */
+    private static Class getDefaultClass(Object bean, String propName)
+    {
+        try
         {
-            for (Iterator it = nestedBeans.entrySet().iterator(); it.hasNext();)
+            PropertyDescriptor desc = PropertyUtils.getPropertyDescriptor(bean, propName);
+            if (desc == null)
             {
-                Map.Entry e = (Map.Entry) it.next();
-                String propName = (String) e.getKey();
-                initProperty(bean, propName, createBean(
-                        (BeanDeclaration) e.getValue(), null));
+                return null;
             }
+            return desc.getPropertyType();
+        }
+        catch (Exception ex)
+        {
+            return null;
         }
     }
 
@@ -215,6 +253,33 @@
     }
 
     /**
+     * Set a property on the bean only if the property exists
+     * @param bean the bean
+     * @param propName the name of the property
+     * @param value the property's value
+     * @throws ConfigurationRuntimeException if the property is not writeable or
+     * an error occurred
+     */
+    public static void setProperty(Object bean, String propName, Object value)
+    {
+        if (PropertyUtils.isWriteable(bean, propName))
+        {
+            try
+            {
+                BeanUtils.setProperty(bean, propName, value);
+            }
+            catch (IllegalAccessException iaex)
+            {
+                throw new ConfigurationRuntimeException(iaex);
+            }
+            catch (InvocationTargetException itex)
+            {
+                throw new ConfigurationRuntimeException(itex);
+            }
+        }
+    }
+
+    /**
      * The main method for creating and initializing beans from a configuration.
      * This method will return an initialized instance of the bean class
      * specified in the passed in bean declaration. If this declaration does not

Added: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/interpol/ExprLookup.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/interpol/ExprLookup.java?rev=764168&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/interpol/ExprLookup.java (added)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/interpol/ExprLookup.java Sat Apr 11 06:23:13 2009
@@ -0,0 +1,290 @@
+/*
+ * 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;
+
+import org.apache.commons.lang.text.StrLookup;
+import org.apache.commons.lang.text.StrSubstitutor;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.ClassUtils;
+import org.apache.commons.configuration.AbstractConfiguration;
+import org.apache.commons.configuration.ConfigurationRuntimeException;
+import org.apache.commons.jexl.JexlHelper;
+import org.apache.commons.jexl.JexlContext;
+import org.apache.commons.jexl.Expression;
+import org.apache.commons.jexl.ExpressionFactory;
+
+import java.util.Iterator;
+import java.util.ArrayList;
+
+/**
+ * Lookup that allows expressions to be evaluated.
+ *
+ * <pre>
+ *     ExprLookup.Variables vars = new ExprLookup.Variables();
+ *     vars.add(new ExprLookup.Variable("String", org.apache.commons.lang.StringUtils.class));
+ *     vars.add(new ExprLookup.Variable("Util", new Utility("Hello")));
+ *     vars.add(new ExprLookup.Variable("System", "Class:java.lang.System"));
+ *     XMLConfiguration config = new XMLConfiguration(TEST_FILE);
+ *     config.setLogger(log);
+ *     ExprLookup lookup = new ExprLookup(vars);
+ *     lookup.setConfiguration(config);
+ *     String str = lookup.lookup("'$[element] ' + String.trimToEmpty('$[space.description]')");
+ * </pre>
+ *
+ * In the example above TEST_FILE contains xml that looks like:
+ * <pre>
+ * &lt;configuration&gt;
+ *   &lt;element&gt;value&lt;/element&gt;
+ *   &lt;space xml:space="preserve"&gt;
+ *     &lt;description xml:space="default"&gt;     Some text      &lt;/description&gt;
+ *   &lt;/space&gt;
+ * &lt;/configuration&gt;
+ * </pre>
+ *
+ * The result will be "value Some text".
+ *
+ * This lookup uses Apache Commons Jexl and requires that the dependency be added to any
+ * projects which use this.
+ *
+ * @since 1.7
+ * @author <a
+ * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
+ * @version $Id:  $
+ */
+public class ExprLookup extends StrLookup
+{
+    /** Prefix to identify a Java Class object */
+    private static final String CLASS = "Class:";
+
+    /** The default prefix for subordinate lookup expressions */
+    private static final String DEFAULT_PREFIX = "$[";
+
+    /** The default suffix for subordinate lookup expressions */
+    private static final String DEFAULT_SUFFIX = "]";
+
+    /** Configuration being operated on */
+    private AbstractConfiguration configuration;
+
+    /** The JexlContext */
+    private JexlContext context = JexlHelper.createContext();
+
+    /** The String to use to start subordinate lookup expressions */
+    private String prefixMatcher = DEFAULT_PREFIX;
+
+    /** The String to use to terminate subordinate lookup expressions */
+    private String suffixMatcher = DEFAULT_SUFFIX;
+
+    /**
+     * The default constructor. Will get used when the Lookup is constructed via
+     * configuration.
+     */
+    public ExprLookup()
+    {
+    }
+
+    /**
+     * Constructor for use by applications.
+     * @param list The list of objects to be accessible in expressions.
+     */
+    public ExprLookup(Variables list)
+    {
+        setVariables(list);
+    }
+
+    /**
+     * Constructor for use by applications.
+     * @param list The list of objects to be accessible in expressions.
+     * @param prefix The prefix to use for subordinate lookups.
+     * @param suffix The suffix to use for subordinate lookups.
+     */
+    public ExprLookup(Variables list, String prefix, String suffix)
+    {
+        this(list);
+        setVariablePrefixMatcher(prefix);
+        setVariableSuffixMatcher(suffix);
+    }
+
+    /**
+     * Set the prefix to use to identify subordinate expressions. This cannot be the
+     * same as the prefix used for the primary expression.
+     * @param prefix The String identifying the beginning of the expression.
+     */
+    public void setVariablePrefixMatcher(String prefix)
+    {
+        prefixMatcher = prefix;
+    }
+
+
+    /**
+     * Set the suffix to use to identify subordinate expressions. This cannot be the
+     * same as the suffix used for the primary expression.
+     * @param suffix The String identifying the end of the expression.
+     */
+    public void setVariableSuffixMatcher(String suffix)
+    {
+        suffixMatcher = suffix;
+    }
+
+    /**
+     * Add the Variables that will be accessible within expressions.
+     * @param list The list of Variables.
+     */
+    public void setVariables(Variables list)
+    {
+        Iterator iter = list.iterator();
+        while (iter.hasNext())
+        {
+            Variable var = (Variable) iter.next();
+            context.getVars().put(var.getName(), var.getValue());
+        }
+    }
+
+    /**
+     * Returns the list of Variables that are accessible within expressions.
+     * @return the List of Variables that are accessible within expressions.
+     */
+    public Variables getVariables()
+    {
+        return null;
+    }
+
+    /**
+     * Set the configuration to be used to interpolate subordinate expressiosn.
+     * @param config The Configuration.
+     */
+    public void setConfiguration(AbstractConfiguration config)
+    {
+        this.configuration = config;
+    }
+
+    /**
+     * Evaluates the expression.
+     * @param var The expression.
+     * @return The String result of the expression.
+     */
+    public String lookup(String var)
+    {
+        ConfigurationInterpolator interp = configuration.getInterpolator();
+        StrSubstitutor subst = new StrSubstitutor(interp, prefixMatcher, suffixMatcher,
+                StrSubstitutor.DEFAULT_ESCAPE);
+
+        String result = subst.replace(var);
+
+        try
+        {
+            Expression exp = ExpressionFactory.createExpression(result);
+            result = (String) exp.evaluate(context);
+        }
+        catch (Exception e)
+        {
+            configuration.getLogger().debug("Error encountered evaluation " + result, e);
+        }
+
+        return result;
+    }
+
+    /**
+     * List wrapper used to allow the Variables list to be created as beans in
+     * DefaultConfigurationBuilder.
+     */
+    public static class Variables extends ArrayList
+    {
+        public void setVariable(Variable var)
+        {
+            add(var);
+        }
+
+        public Variable getVariable()
+        {
+            if (size() > 0)
+            {
+                return (Variable) get(size() - 1);
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+    }
+
+    /**
+     * The key and corresponding object that will be made available to the
+     * JexlContext for use in expressions.
+     */
+    public static class Variable
+    {
+        /** The name to be used in expressions */
+        private String key;
+
+        /** The object to be accessed in expressions */
+        private Object value;
+
+        public Variable()
+        {
+        }
+
+        public Variable(String name, Object value)
+        {
+            setName(name);
+            setValue(value);
+        }
+
+        public String getName()
+        {
+            return key;
+        }
+
+        public void setName(String name)
+        {
+            this.key = name;
+        }
+
+        public Object getValue()
+        {
+            return value;
+        }
+
+        public void setValue(Object value) throws ConfigurationRuntimeException
+        {
+            try
+            {
+                if (!(value instanceof String))
+                {
+                    this.value = value;
+                    return;
+                }
+                String val = (String) value;
+                String name = StringUtils.removeStartIgnoreCase(val, CLASS);
+                Class clazz = ClassUtils.getClass(name);
+                if (name.length() == val.length())
+                {
+                    this.value = clazz.newInstance();
+                }
+                else
+                {
+                    this.value = clazz;
+                }
+            }
+            catch (Exception e)
+            {
+                throw new ConfigurationRuntimeException("Unable to create " + value, e);
+            }
+
+        }
+    }
+}

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/reloading/VFSFileMonitorReloadingStrategy.java Sat Apr 11 06:23:13 2009
@@ -90,7 +90,7 @@
      */
     public void setConfiguration(FileConfiguration configuration)
     {
-        if (configuration instanceof FileSystemBased)
+        if (configuration == null || configuration instanceof FileSystemBased)
         {
             this.configuration = configuration;
         }
@@ -105,6 +105,10 @@
      */
     public void init()
     {
+        if (configuration.getURL() == null && configuration.getFileName() == null)
+        {
+            return;
+        }
         if (this.configuration == null)
         {
             throw new IllegalStateException("No configuration has been set for this strategy");
@@ -134,6 +138,10 @@
             FileSystem fs = ((FileSystemBased) configuration).getFileSystem();
             String uri = fs.getPath(null, configuration.getURL(), configuration.getBasePath(),
                 configuration.getFileName());
+            if (uri == null)
+            {
+                throw new ConfigurationRuntimeException("Unable to determine file to monitor");
+            }
             FileObject file = fsManager.resolveFile(uri);
             file.getFileSystem().addListener(file, this);
             fm.addFile(file);

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/MergeCombiner.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/MergeCombiner.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/MergeCombiner.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/MergeCombiner.java Sat Apr 11 06:23:13 2009
@@ -54,13 +54,6 @@
 
     public ConfigurationNode combine(ConfigurationNode node1, ConfigurationNode node2)
     {
-        ConfigurationNode result = doCombine(node1, node2);
-        printTree(result);
-        return result;
-    }
-
-    public ConfigurationNode doCombine(ConfigurationNode node1, ConfigurationNode node2)
-    {
         ViewNode result = createViewNode();
         result.setName(node1.getName());
         result.setValue(node1.getValue());
@@ -74,7 +67,7 @@
             ConfigurationNode child2 = canCombine(node1, node2, child1, children2);
             if (child2 != null)
             {
-                result.addChild(doCombine(child1, child2));
+                result.addChild(combine(child1, child2));
                 children2.remove(child2);
             }
             else

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/NodeCombiner.java Sat Apr 11 06:23:13 2009
@@ -19,8 +19,6 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
-import java.util.Iterator;
-import java.io.PrintStream;
 
 /**
  * <p>
@@ -57,9 +55,6 @@
     /** Stores a list with node names that are known to be list nodes. */
     protected Set listNodes;
 
-    /** Stream to write debug output to */
-    private PrintStream debugStream;
-
     /**
      * Creates a new instance of <code>NodeCombiner</code>.
      */
@@ -126,54 +121,4 @@
     {
         return new ViewNode();
     }
-
-    /**
-     * Set the output stream to write the tree to.
-     * @param stream The OutputStream.
-     */
-    public void setDebugStream(PrintStream stream)
-    {
-        this.debugStream = stream;
-    }
-
-    protected void printTree(ConfigurationNode result)
-    {
-        if (debugStream != null)
-        {
-            printTree("", result);
-        }
-    }
-
-    private void printTree(String indent, ConfigurationNode result)
-    {
-        StringBuffer buffer = new StringBuffer(indent).append("<").append(result.getName());
-        Iterator iter = result.getAttributes().iterator();
-        while (iter.hasNext())
-        {
-            ConfigurationNode node = (ConfigurationNode) iter.next();
-            buffer.append(" ").append(node.getName()).append("='").append(node.getValue()).append("'");
-        }
-        buffer.append(">");
-        debugStream.print(buffer.toString());
-        if (result.getValue() != null)
-        {
-            debugStream.print(result.getValue());
-        }
-        boolean newline = false;
-        if (result.getChildrenCount() > 0)
-        {
-            debugStream.print("\n");
-            iter = result.getChildren().iterator();
-            while (iter.hasNext())
-            {
-                printTree(indent + "  ", (ConfigurationNode) iter.next());
-            }
-            newline = true;
-        }
-        if (newline)
-        {
-            debugStream.print(indent);
-        }
-        debugStream.println("</" + result.getName() + ">");
-    }
 }

Added: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/TreeUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/TreeUtils.java?rev=764168&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/TreeUtils.java (added)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/tree/TreeUtils.java Sat Apr 11 06:23:13 2009
@@ -0,0 +1,82 @@
+/*
+ * 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.tree;
+
+import java.io.PrintStream;
+import java.util.Iterator;
+
+/**
+ * Utility methods.
+ * @author <a
+ * href="http://commons.apache.org/configuration/team-list.html">Commons
+ * Configuration team</a>
+ * @version $Id: NodeCombiner.java 761171 2009-04-02 05:42:41Z rgoers $
+ * @since 1.6
+ */
+public final class TreeUtils
+{
+    /** Prevent creating this class. */
+    private TreeUtils()
+    {
+    }
+
+    /**
+     * Print out the data in the configuration.
+     * @param stream The OutputStream.
+     * @param result The root node of the tree.
+     */
+    public static void printTree(PrintStream stream, ConfigurationNode result)
+    {
+        if (stream != null)
+        {
+            printTree(stream, "", result);
+        }
+    }
+
+    private static void printTree(PrintStream stream, String indent, ConfigurationNode result)
+    {
+        StringBuffer buffer = new StringBuffer(indent).append("<").append(result.getName());
+        Iterator iter = result.getAttributes().iterator();
+        while (iter.hasNext())
+        {
+            ConfigurationNode node = (ConfigurationNode) iter.next();
+            buffer.append(" ").append(node.getName()).append("='").append(node.getValue()).append("'");
+        }
+        buffer.append(">");
+        stream.print(buffer.toString());
+        if (result.getValue() != null)
+        {
+            stream.print(result.getValue());
+        }
+        boolean newline = false;
+        if (result.getChildrenCount() > 0)
+        {
+            stream.print("\n");
+            iter = result.getChildren().iterator();
+            while (iter.hasNext())
+            {
+                printTree(stream, indent + "  ", (ConfigurationNode) iter.next());
+            }
+            newline = true;
+        }
+        if (newline)
+        {
+            stream.print(indent);
+        }
+        stream.println("</" + result.getName() + ">");
+    }
+}

Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDefaultConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDefaultConfigurationBuilder.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDefaultConfigurationBuilder.java (original)
+++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestDefaultConfigurationBuilder.java Sat Apr 11 06:23:13 2009
@@ -18,6 +18,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.StringWriter;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -33,6 +34,13 @@
 import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
 import org.apache.commons.lang.text.StrLookup;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.impl.Log4JLogger;
+import org.apache.log4j.WriterAppender;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Level;
+import org.apache.log4j.SimpleLayout;
 
 /**
  * Test class for DefaultConfigurationBuilder.
@@ -82,6 +90,9 @@
     private static final File MULTI_TENENT_FILE = new File(
             "conf/testMultiTenentConfigurationBuilder.xml");
 
+    private static final File EXPRESSION_FILE = new File(
+            "conf/testExpression.xml");
+
     /** Constant for the name of an optional configuration.*/
     private static final String OPTIONAL_NAME = "optionalConfig";
 
@@ -873,12 +884,25 @@
     public void testMultiTenentConfiguration3() throws Exception
     {
         factory.setFile(MULTI_TENENT_FILE);
+        StringWriter writer = new StringWriter();
+        WriterAppender app = new WriterAppender(new SimpleLayout(), writer);
+        Log log = LogFactory.getLog("TestLogger");
+        Logger logger = ((Log4JLogger)log).getLogger();
+        logger.addAppender(app);
+        logger.setLevel(Level.DEBUG);
+        logger.setAdditivity(false);
+
         System.setProperty("Id", "1005");
 
         CombinedConfiguration config = factory.getConfiguration(true);
         assertTrue("Incorrect configuration", config instanceof DynamicCombinedConfiguration);
 
         verify("1001", config, 15);
+        String xml = writer.getBuffer().toString();
+        assertNotNull("No XML returned", xml);
+        assertTrue("Incorect configuration data", xml.contains("<rowsPerPage>15</rowsPerPage>"));
+        logger.removeAppender(app);
+        logger.setLevel(Level.OFF);
         verify("1002", config, 25);
         verify("1003", config, 35);
         verify("1004", config, 50);
@@ -902,19 +926,26 @@
     {
         factory.setFile(MULTI_TENENT_FILE);
         System.setProperty("Id", "1004");
+        Map map = new HashMap();
+        map.put("default", "${colors.header4}");
+        map.put("background", "#40404040");
+        map.put("text", "#000000");
+        map.put("header", "#444444");
 
         CombinedConfiguration config = factory.getConfiguration(true);
         assertTrue("Incorrect configuration", config instanceof DynamicCombinedConfiguration);
 
         List list = config.configurationsAt("colors/*");
         Iterator iter = list.iterator();
-        System.out.println("Color nodes");
         while (iter.hasNext())
         {
             SubnodeConfiguration sub = (SubnodeConfiguration)iter.next();
             ConfigurationNode node = sub.getRootNode();
             String value = (node.getValue() == null) ? "null" : node.getValue().toString();
-            System.out.println(node.getName() + "=" + value);
+            if (map.containsKey(node.getName()))
+            {
+                assertEquals(map.get(node.getName()), value);
+            }
         }
 
     }
@@ -935,6 +966,18 @@
         assertEquals("a\\,b\\,c", config.getString("split/list2"));
     }
 
+    public void testExpression() throws Exception
+    {
+        factory.setFile(EXPRESSION_FILE);
+        factory.setAttributeSplittingDisabled(true);
+        System.getProperties().remove("Id");
+
+        CombinedConfiguration config = factory.getConfiguration(true);
+        assertTrue("Incorrect configuration", config instanceof DynamicCombinedConfiguration);
+
+        verify("1001", config, 15);
+    }
+
     private void verify(String key, CombinedConfiguration config, int rows)
     {
         System.setProperty("Id", key);

Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java (original)
+++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestVFSConfigurationBuilder.java Sat Apr 11 06:23:13 2009
@@ -18,6 +18,10 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.Reader;
+import java.io.FileReader;
+import java.io.Writer;
+import java.io.FileWriter;
 import java.util.Collection;
 import java.util.Set;
 import java.util.List;
@@ -81,6 +85,9 @@
     private static final File FILESYSTEM_FILE = new File(
             "conf/testFileSystem.xml");
 
+    private static final File FILEMONITOR_FILE = new File(
+            "target/test-classes/testFileMonitorConfigurationBuilder.xml");
+
     /** Constant for the name of an optional configuration.*/
     private static final String OPTIONAL_NAME = "optionalConfig";
 
@@ -966,6 +973,48 @@
         }
     }
 
+    public void testFileMonitor() throws Exception
+    {
+
+        // create a new configuration
+        File input = new File("target/test-classes/testMultiConfiguration_1001.xml");
+        File output = new File("target/test-classes/testwrite/testMultiConfiguration_1001.xml");
+        output.getParentFile().mkdir();
+        copyFile(input, output);
+
+        factory.setFile(FILEMONITOR_FILE);
+        FileSystem.resetDefaultFileSystem();
+        System.getProperties().remove("Id");
+
+        CombinedConfiguration config = factory.getConfiguration(true);
+        assertNotNull(config);
+        verify("1001", config, 15);
+
+        // Allow time for FileMonitor to set up.
+        Thread.sleep(1000);
+        XMLConfiguration x = new XMLConfiguration(output);
+        x.setProperty("rowsPerPage", "50");
+        x.save();
+        // Let FileMonitor detect the change.
+        Thread.sleep(2000);
+        verify("1001", config, 50);
+    }
+
+    private void copyFile(File input, File output) throws IOException
+    {
+        Reader reader = new FileReader(input);
+        Writer writer = new FileWriter(output);
+        char[] buffer = new char[4096];
+        int n = 0;
+        while (-1 != (n = reader.read(buffer)))
+        {
+            writer.write(buffer, 0, n);
+        }
+        reader.close();
+        writer.close();
+    }
+
+
     private void verify(String key, CombinedConfiguration config, int rows)
     {
         System.setProperty("Id", key);

Added: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/interpol/TestExprLookup.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/interpol/TestExprLookup.java?rev=764168&view=auto
==============================================================================
--- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/interpol/TestExprLookup.java (added)
+++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/interpol/TestExprLookup.java Sat Apr 11 06:23:13 2009
@@ -0,0 +1,107 @@
+/*
+ * 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;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.StringWriter;
+
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.impl.Log4JLogger;
+import org.apache.log4j.WriterAppender;
+import org.apache.log4j.SimpleLayout;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Level;
+import org.apache.log4j.ConsoleAppender;
+
+/**
+ * Test class for ExprLookup.
+ *
+ * @version $Id:  $
+ */
+public class TestExprLookup extends TestCase
+{
+    private static File TEST_FILE = new File("conf/test.xml");
+
+    private static String PATTERN1 =
+        "String.replace(Util.message, 'Hello', 'Goodbye') + System.getProperty('user.name')";
+    private static String PATTERN2 =
+        "'$[element] ' + String.trimToEmpty('$[space.description]')";
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+    }
+
+    /**
+     * Clears the test environment. Here the static cache of the constant lookup
+     * class is wiped out.
+     */
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+
+    public void testLookup() throws Exception
+    {
+        ConsoleAppender app = new ConsoleAppender(new SimpleLayout());
+        Log log = LogFactory.getLog("TestLogger");
+        Logger logger = ((Log4JLogger)log).getLogger();
+        logger.addAppender(app);
+        logger.setLevel(Level.DEBUG);
+        logger.setAdditivity(false);
+        ExprLookup.Variables vars = new ExprLookup.Variables();
+        vars.add(new ExprLookup.Variable("String", org.apache.commons.lang.StringUtils.class));
+        vars.add(new ExprLookup.Variable("Util", new Utility("Hello")));
+        vars.add(new ExprLookup.Variable("System", "Class:java.lang.System"));
+        XMLConfiguration config = new XMLConfiguration(TEST_FILE);
+        config.setLogger(log);
+        ExprLookup lookup = new ExprLookup(vars);
+        lookup.setConfiguration(config);
+        String str = lookup.lookup(PATTERN1);
+        assertTrue(str.startsWith("Goodbye"));
+        str = lookup.lookup(PATTERN2);
+        assertTrue("Incorrect value: " + str, str.equals("value Some text"));
+        logger.removeAppender(app);
+
+    }
+
+
+
+    public static class Utility
+    {
+        String message;
+
+        public Utility(String msg)
+        {
+            this.message = msg;
+        }
+
+        public String getMessage()
+        {
+            return message;
+        }
+
+        public String str(String str)
+        {
+            return str;
+        }
+    }
+}

Modified: commons/proper/configuration/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/xdocs/changes.xml?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/xdocs/changes.xml (original)
+++ commons/proper/configuration/trunk/xdocs/changes.xml Sat Apr 11 06:23:13 2009
@@ -23,6 +23,11 @@
 
   <body>
     <release version="1.7" date="in SVN" description="">
+      <action dev="rgoers" type="add" issue="CONFIGURATION-380">
+        Add ExprLookup to allow expressions to be evaluated in configurations. When 
+        used, this requires that Apache Commons Jexl be added as a dependency to
+        projects using Commons Configuration.
+      </action>
       <action dev="oheger" type="add" issue="CONFIGURATION-374">
         MapConfiguration now provides a way of controlling the trimming
         behavior.

Modified: commons/proper/configuration/trunk/xdocs/userguide/howto_basicfeatures.xml
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/xdocs/userguide/howto_basicfeatures.xml?rev=764168&r1=764167&r2=764168&view=diff
==============================================================================
--- commons/proper/configuration/trunk/xdocs/userguide/howto_basicfeatures.xml (original)
+++ commons/proper/configuration/trunk/xdocs/userguide/howto_basicfeatures.xml Sat Apr 11 06:23:13 2009
@@ -271,7 +271,80 @@
       which are then only used by this configuration instance.
     </p>
     </subsection>
-    </section>
+    <subsection name="Using Expressions">
+      <p>
+        In addition to the simple lookup mechanisms previously described, Commond Configuration
+        provides <code>ExprLookup</code> which uses <a href="http://commons.apache.org/jexl">Apache
+        Commons Jexl</a> to allow expressions to be resolved wherever a StrLookup is allowed. This
+        example shows an alternate way of obtaining a system property if the ExprLookup is
+        configured.
+      </p>
+<source><![CDATA[
+user.file = ${expr:System.getProperty("user.home"}/settings.xml
+]]></source>
+      <p>
+        <code>ExprLookup</code> is not enabled by default, it must be manually added or configured via
+        <code>DefaultConfigurationBuilder</code>. Builds that use Maven 2 and reference Commons
+        Configuration will not include a dependency on Jexl, so if this feature is used the
+        dependency on Jexl must be manually added to the project.
+      </p>
+      <p>
+        When using <code>DefaultConfigurationBuilder</code> adding <code>ExprLookup</code> is
+        straightforward.
+      </p>
+<source><![CDATA[
+<configuration>
+  <header>
+    <result/>
+    <lookups>
+      <lookup config-prefix="expr"
+              config-class="org.apache.commons.configuration.interpol.ExprLookup">
+        <variables>
+          <variable name="System" value="Class:java.lang.System"/>
+          <variable name"net" value="Class:java.net.InetAddress"/>
+          <variable name="String" value="Class:org.apache.commons.lang.StringUtils"/>
+        </variables>
+      </lookup>
+    </lookups>
+  </header>
+  <override>
+    <xml fileName="${expr:System.getProperty('basePath') +
+         String.lowercase(net.localHost.hostName) + '/testMultiConfiguration_default.xml'}"
+         config-name="defaultConfig" delimiterParsingDisabled="true">
+    </xml>
+  </override>
+</configuration>
+]]></source>
+      <p>
+        The example above shows how to invoke static methods during expression evaluation. The
+        next example shows mixing expression evaluation with a subordinate lookup to
+        obtain the "basePath" system property. Note the difference in how the
+        system property was obtained in the previous example.
+      </p>
+<source><![CDATA[
+<configuration>
+  <header>
+    <result/>
+    <lookups>
+      <lookup config-prefix="expr"
+              config-class="org.apache.commons.configuration.interpol.ExprLookup">
+        <variables>
+          <variable name"net" value="Class:java.net.InetAddress"/>
+          <variable name="String" value="Class:org.apache.commons.lang.StringUtils"/>
+        </variables>
+      </lookup>
+    </lookups>
+  </header>
+  <override>
+    <xml fileName="${expr:$[sys:basePath] + 
+         String.lowercase(net.localHost.hostName) + '/testMultiConfiguration_default.xml'}"
+         config-name="defaultConfig" delimiterParsingDisabled="true">
+    </xml>
+  </override>
+</configuration>
+]]></source>
+    </subsection>
+  </section>
 </body>
 
 </document>
\ No newline at end of file