You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2014/04/20 21:32:10 UTC

svn commit: r1588831 [3/11] - in /commons/proper/configuration/trunk: ./ src/main/java/org/apache/commons/configuration/ src/main/java/org/apache/commons/configuration/beanutils/ src/main/java/org/apache/commons/configuration/builder/combined/ src/main...

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/CombinedConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/CombinedConfiguration.java?rev=1588831&r1=1588830&r2=1588831&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/CombinedConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/CombinedConfiguration.java Sun Apr 20 19:32:08 2014
@@ -21,6 +21,7 @@ import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -31,15 +32,15 @@ import org.apache.commons.configuration.
 import org.apache.commons.configuration.event.EventSource;
 import org.apache.commons.configuration.ex.ConfigurationRuntimeException;
 import org.apache.commons.configuration.sync.LockMode;
-import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.DefaultConfigurationKey;
-import org.apache.commons.configuration.tree.DefaultConfigurationNode;
 import org.apache.commons.configuration.tree.DefaultExpressionEngine;
 import org.apache.commons.configuration.tree.ExpressionEngine;
+import org.apache.commons.configuration.tree.ImmutableNode;
 import org.apache.commons.configuration.tree.NodeCombiner;
+import org.apache.commons.configuration.tree.NodeTreeWalker;
+import org.apache.commons.configuration.tree.QueryResult;
 import org.apache.commons.configuration.tree.TreeUtils;
 import org.apache.commons.configuration.tree.UnionCombiner;
-import org.apache.commons.configuration.tree.ViewNode;
 
 /**
  * <p>
@@ -71,21 +72,20 @@ import org.apache.commons.configuration.
  * {@code NodeCombiner}, this may be a complex operation.
  * </p>
  * <p>
- * Because of the way a {@code CombinedConfiguration} is working it has
- * more or less view character: it provides a logic view on the configurations
- * it contains. In this constellation not all methods defined for hierarchical
+ * Because of the way a {@code CombinedConfiguration} is working it has more or
+ * less view character: it provides a logic view on the configurations it
+ * contains. In this constellation not all methods defined for hierarchical
  * configurations - especially methods that update the stored properties - can
  * be implemented in a consistent manner. Using such methods (like
  * {@code addProperty()}, or {@code clearProperty()} on a
- * {@code CombinedConfiguration} is not strictly forbidden, however,
- * depending on the current {@link NodeCombiner} and the involved
- * properties, the results may be different than expected. Some examples may
- * illustrate this:
+ * {@code CombinedConfiguration} is not strictly forbidden, however, depending
+ * on the current {@link NodeCombiner} and the involved properties, the results
+ * may be different than expected. Some examples may illustrate this:
  * </p>
  * <p>
  * <ul>
- * <li>Imagine a {@code CombinedConfiguration} <em>cc</em> containing
- * two child configurations with the following content:
+ * <li>Imagine a {@code CombinedConfiguration} <em>cc</em> containing two child
+ * configurations with the following content:
  * <dl>
  * <dt>user.properties</dt>
  * <dd>
@@ -108,46 +108,47 @@ import org.apache.commons.configuration.
  * </dd>
  * </dl>
  * As a {@code NodeCombiner} a
- * {@link org.apache.commons.configuration.tree.OverrideCombiner OverrideCombiner}
- * is used. This combiner will ensure that defined user settings take precedence
- * over the default values. If the resulting {@code CombinedConfiguration}
- * is queried for the background color, {@code blue} will be returned
- * because this value is defined in {@code user.properties}. Now
- * consider what happens if the key {@code gui.background} is removed
- * from the {@code CombinedConfiguration}:
+ * {@link org.apache.commons.configuration.tree.OverrideCombiner
+ * OverrideCombiner} is used. This combiner will ensure that defined user
+ * settings take precedence over the default values. If the resulting
+ * {@code CombinedConfiguration} is queried for the background color,
+ * {@code blue} will be returned because this value is defined in
+ * {@code user.properties}. Now consider what happens if the key
+ * {@code gui.background} is removed from the {@code CombinedConfiguration}:
  *
- * <pre>cc.clearProperty("gui.background");</pre>
+ * <pre>
+ * cc.clearProperty(&quot;gui.background&quot;);
+ * </pre>
  *
- * Will a {@code cc.containsKey("gui.background")} now return <b>false</b>?
- * No, it won't! The {@code clearProperty()} operation is executed on the
- * node set of the combined configuration, which was constructed from the nodes
- * of the two child configurations. It causes the value of the
- * <em>background</em> node to be cleared, which is also part of the first
- * child configuration. This modification of one of its child configurations
- * causes the {@code CombinedConfiguration} to be re-constructed. This
- * time the {@code OverrideCombiner} cannot find a
- * {@code gui.background} property in the first child configuration, but
- * it finds one in the second, and adds it to the resulting combined
- * configuration. So the property is still present (with a different value now).</li>
- * <li>{@code addProperty()} can also be problematic: Most node
- * combiners use special view nodes for linking parts of the original
- * configurations' data together. If new properties are added to such a special
- * node, they do not belong to any of the managed configurations and thus hang
- * in the air. Using the same configurations as in the last example, the
- * statement
+ * Will a {@code cc.containsKey("gui.background")} now return <b>false</b>? No,
+ * it won't! The {@code clearProperty()} operation is executed on the node set
+ * of the combined configuration, which was constructed from the nodes of the
+ * two child configurations. It causes the value of the <em>background</em> node
+ * to be cleared, which is also part of the first child configuration. This
+ * modification of one of its child configurations causes the
+ * {@code CombinedConfiguration} to be re-constructed. This time the
+ * {@code OverrideCombiner} cannot find a {@code gui.background} property in the
+ * first child configuration, but it finds one in the second, and adds it to the
+ * resulting combined configuration. So the property is still present (with a
+ * different value now).</li>
+ * <li>{@code addProperty()} can also be problematic: Most node combiners use
+ * special view nodes for linking parts of the original configurations' data
+ * together. If new properties are added to such a special node, they do not
+ * belong to any of the managed configurations and thus hang in the air. Using
+ * the same configurations as in the last example, the statement
  *
  * <pre>
- * addProperty("database.user", "scott");
+ * addProperty(&quot;database.user&quot;, &quot;scott&quot;);
  * </pre>
  *
  * would cause such a hanging property. If now one of the child configurations
- * is changed and the {@code CombinedConfiguration} is re-constructed,
- * this property will disappear! (Add operations are not problematic if they
- * result in a child configuration being updated. For instance an
- * {@code addProperty("home.url", "localhost");} will alter the second
- * child configuration - because the prefix <em>home</em> is here already
- * present; when the {@code CombinedConfiguration} is re-constructed,
- * this change is taken into account.)</li>
+ * is changed and the {@code CombinedConfiguration} is re-constructed, this
+ * property will disappear! (Add operations are not problematic if they result
+ * in a child configuration being updated. For instance an
+ * {@code addProperty("home.url", "localhost");} will alter the second child
+ * configuration - because the prefix <em>home</em> is here already present;
+ * when the {@code CombinedConfiguration} is re-constructed, this change is
+ * taken into account.)</li>
  * </ul>
  * Because of such problems it is recommended to perform updates only on the
  * managed child configurations.
@@ -155,26 +156,27 @@ import org.apache.commons.configuration.
  * <p>
  * Whenever the node structure of a {@code CombinedConfiguration} becomes
  * invalid (either because one of the contained configurations was modified or
- * because the {@code invalidate()} method was directly called) an event
- * is generated. So this can be detected by interested event listeners. This
- * also makes it possible to add a combined configuration into another one.
+ * because the {@code invalidate()} method was directly called) an event is
+ * generated. So this can be detected by interested event listeners. This also
+ * makes it possible to add a combined configuration into another one.
  * </p>
  * <p>
  * Notes about thread-safety: This configuration implementation uses a
  * {@code Synchronizer} object to protect instances against concurrent access.
  * The concrete {@code Synchronizer} implementation used determines whether an
- * instance of this class is thread-safe or not. All methods accessing
- * configuration data or querying or altering this configuration's child
- * configurations are guarded by the {@code Synchronizer}. Because a combined
- * configuration operates on node structures partly owned by its child
- * configurations it makes sense that a single {@code Synchronizer} object is
- * used and shared between all involved configurations (including the combined
- * configuration itself). However, this is not enforced.
+ * instance of this class is thread-safe or not. In contrast to other
+ * implementations derived from {@link BaseHierarchicalConfiguration},
+ * thread-safety is an issue here because the nodes structure used by this
+ * configuration has to be constructed dynamically when a child configuration is
+ * changed. Therefore, when multiple threads are involved which also manipulate
+ * one of the child configurations, a proper {@code Synchronizer} object should
+ * be set. Note that the {@code Synchronizer} objects used by the child
+ * configurations do not really matter. Because immutable in-memory nodes
+ * structures are used for them there is no danger that updates on child
+ * configurations could interfere with read operations on the combined
+ * configuration.
  * </p>
  *
- * @author <a
- * href="http://commons.apache.org/configuration/team-list.html">Commons
- * Configuration team</a>
  * @since 1.3
  * @version $Id$
  */
@@ -198,12 +200,13 @@ public class CombinedConfiguration exten
     /** Constant for the default node combiner. */
     private static final NodeCombiner DEFAULT_COMBINER = new UnionCombiner();
 
+    /** Constant for a root node for an empty configuration. */
+    private static final ImmutableNode EMPTY_ROOT = new ImmutableNode.Builder()
+            .create();
+
     /** Stores the combiner. */
     private NodeCombiner nodeCombiner;
 
-    /** Stores the combined root node. */
-    private ConfigurationNode combinedRoot;
-
     /** Stores a list with the contained configurations. */
     private List<ConfigData> configurations;
 
@@ -216,6 +219,9 @@ public class CombinedConfiguration exten
      */
     private ExpressionEngine conversionExpressionEngine;
 
+    /** A flag whether this configuration is up-to-date. */
+    private boolean upToDate;
+
     /**
      * Creates a new instance of {@code CombinedConfiguration} and
      * initializes the combiner to be used.
@@ -644,22 +650,6 @@ public class CombinedConfiguration exten
     }
 
     /**
-     * Returns the configuration root node of this combined configuration. When
-     * starting a read or write operation (by obtaining a corresponding lock for
-     * this configuration) a combined node structure is constructed if necessary
-     * using the current node combiner. This method just returns this combined
-     * node. Note that this method should only be called with a lock held!
-     * Otherwise, result may be <b>null</b> under certain circumstances.
-     *
-     * @return the combined root node
-     */
-    @Override
-    public ConfigurationNode getRootNode()
-    {
-        return combinedRoot;
-    }
-
-    /**
      * Clears this configuration. All contained configurations will be removed.
      */
     @Override
@@ -692,7 +682,6 @@ public class CombinedConfiguration exten
                         .getConfiguration()), cd.getName(), cd.getAt());
             }
 
-            copy.setRootNode(new DefaultConfigurationNode());
             return copy;
         }
         finally
@@ -731,28 +720,63 @@ public class CombinedConfiguration exten
             throw new IllegalArgumentException("Key must not be null!");
         }
 
+        Set<Configuration> sources = getSources(key);
+        if (sources.isEmpty())
+        {
+            return null;
+        }
+        Iterator<Configuration> iterator = sources.iterator();
+        Configuration source = iterator.next();
+        if (iterator.hasNext())
+        {
+            throw new IllegalArgumentException("The key " + key
+                    + " is defined by multiple sources!");
+        }
+        return source;
+    }
+
+    /**
+     * Returns a set with the configuration sources, in which the specified key
+     * is defined. This method determines the configuration nodes that are
+     * identified by the given key. It then determines the configuration sources
+     * to which these nodes belong and adds them to the result set. Note the
+     * following points:
+     * <ul>
+     * <li>If no node object is found for this key, an empty set is returned.</li>
+     * <li>For keys that have been added directly to this combined configuration
+     * and that do not belong to the namespaces defined by existing child
+     * configurations this combined configuration is contained in the result
+     * set.</li>
+     * </ul>
+     *
+     * @param key the key of a configuration property
+     * @return a set with the configuration sources, which contain this property
+     * @since 2.0
+     */
+    public Set<Configuration> getSources(String key)
+    {
         beginRead(false);
         try
         {
-            List<ConfigurationNode> nodes = fetchNodeList(key);
-            if (nodes.isEmpty())
-            {
-                return null;
-            }
+            List<QueryResult<ImmutableNode>> results = fetchNodeList(key);
+            Set<Configuration> sources = new HashSet<Configuration>();
 
-            Iterator<ConfigurationNode> it = nodes.iterator();
-            Configuration source = findSourceConfiguration(it.next());
-            while (it.hasNext())
+            for (QueryResult<ImmutableNode> result : results)
             {
-                Configuration src = findSourceConfiguration(it.next());
-                if (src != source)
+                Set<Configuration> resultSources =
+                        findSourceConfigurations(result.getNode());
+                if (resultSources.isEmpty())
+                {
+                    // key must be defined in combined configuration
+                    sources.add(this);
+                }
+                else
                 {
-                    throw new IllegalArgumentException("The key " + key
-                            + " is defined by multiple sources!");
+                    sources.addAll(resultSources);
                 }
             }
 
-            return source;
+            return sources;
         }
         finally
         {
@@ -777,8 +801,8 @@ public class CombinedConfiguration exten
         boolean lockObtained = false;
         do
         {
-            super.beginRead(optimize);
-            if (combinedRoot != null)
+            super.beginRead(false);
+            if (isUpToDate())
             {
                 lockObtained = true;
             }
@@ -808,9 +832,11 @@ public class CombinedConfiguration exten
 
         try
         {
-            if (combinedRoot == null)
+            if (!isUpToDate())
             {
-                combinedRoot = constructCombinedNode();
+                getSubConfigurationParentModel().replaceRoot(
+                        constructCombinedNode(), this);
+                upToDate = true;
             }
         }
         catch (RuntimeException rex)
@@ -821,6 +847,18 @@ public class CombinedConfiguration exten
     }
 
     /**
+     * Returns a flag whether this configuration has been invalidated. This
+     * means that the combined nodes structure has to be rebuilt before the
+     * configuration can be accessed.
+     *
+     * @return a flag whether this configuration is invalid
+     */
+    private boolean isUpToDate()
+    {
+        return upToDate;
+    }
+
+    /**
      * Marks this configuration as invalid. This means that the next access
      * re-creates the root node. An invalidate event is also fired. Note:
      * This implementation expects that an exclusive (write) lock is held on
@@ -828,7 +866,7 @@ public class CombinedConfiguration exten
      */
     private void invalidateInternal()
     {
-        combinedRoot = null;
+        upToDate = false;
         fireEvent(EVENT_COMBINED_INVALIDATE, null, null, false);
     }
 
@@ -847,7 +885,7 @@ public class CombinedConfiguration exten
      *
      * @return the combined root node
      */
-    private ConfigurationNode constructCombinedNode()
+    private ImmutableNode constructCombinedNode()
     {
         if (getNumberOfConfigurationsInternal() < 1)
         {
@@ -855,13 +893,13 @@ public class CombinedConfiguration exten
             {
                 getLogger().debug("No configurations defined for " + this);
             }
-            return new ViewNode();
+            return EMPTY_ROOT;
         }
 
         else
         {
             Iterator<ConfigData> it = configurations.iterator();
-            ConfigurationNode node = it.next().getTransformedRoot();
+            ImmutableNode node = it.next().getTransformedRoot();
             while (it.hasNext())
             {
                 node = nodeCombiner.combine(node,
@@ -879,33 +917,30 @@ public class CombinedConfiguration exten
     }
 
     /**
-     * Determines the configuration that owns the specified node.
+     * Determines the configurations to which the specified node belongs. This
+     * is done by inspecting the nodes structures of all child configurations.
      *
      * @param node the node
-     * @return the owning configuration
+     * @return a set with the owning configurations
      */
-    private Configuration findSourceConfiguration(ConfigurationNode node)
+    private Set<Configuration> findSourceConfigurations(ImmutableNode node)
     {
-        ConfigurationNode root = null;
-        ConfigurationNode current = node;
-
-        // find the root node in this hierarchy
-        while (current != null)
-        {
-            root = current;
-            current = current.getParentNode();
-        }
+        Set<Configuration> result = new HashSet<Configuration>();
+        FindNodeVisitor<ImmutableNode> visitor =
+                new FindNodeVisitor<ImmutableNode>(node);
 
-        // Check with the root nodes of the child configurations
         for (ConfigData cd : configurations)
         {
-            if (root == cd.getRootNode())
+            NodeTreeWalker.INSTANCE.walkBFS(cd.getRootNode(), visitor,
+                    getModel().getNodeHandler());
+            if (visitor.isFound())
             {
-                return cd.getConfiguration();
+                result.add(cd.getConfiguration());
+                visitor.reset();
             }
         }
 
-        return this;
+        return result;
     }
 
     /**
@@ -967,7 +1002,7 @@ public class CombinedConfiguration exten
         private final String at;
 
         /** Stores the root node for this child configuration.*/
-        private ConfigurationNode rootNode;
+        private ImmutableNode rootNode;
 
         /**
          * Creates a new instance of {@code ConfigData} and initializes
@@ -1021,7 +1056,7 @@ public class CombinedConfiguration exten
          * @return the root node of this child configuration
          * @since 1.5
          */
-        public ConfigurationNode getRootNode()
+        public ImmutableNode getRootNode()
         {
             return rootNode;
         }
@@ -1033,41 +1068,83 @@ public class CombinedConfiguration exten
          *
          * @return the transformed root node
          */
-        public ConfigurationNode getTransformedRoot()
+        public ImmutableNode getTransformedRoot()
         {
-            ViewNode result = new ViewNode();
-            ViewNode atParent = result;
+            ImmutableNode configRoot = getRootNodeOfConfiguration();
+            return (atPath == null) ? configRoot : prependAtPath(configRoot);
+        }
 
-            if (atPath != null)
+        /**
+         * Prepends the at path to the given node.
+         *
+         * @param node the root node of the represented configuration
+         * @return the new root node including the at path
+         */
+        private ImmutableNode prependAtPath(ImmutableNode node)
+        {
+            ImmutableNode.Builder pathBuilder = new ImmutableNode.Builder();
+            Iterator<String> pathIterator = atPath.iterator();
+            prependAtPathComponent(pathBuilder, pathIterator.next(),
+                    pathIterator, node);
+            return new ImmutableNode.Builder(1).addChild(pathBuilder.create())
+                    .create();
+        }
+
+        /**
+         * Handles a single component of the at path. A corresponding node is
+         * created and added to the hierarchical path to the original root node
+         * of the configuration.
+         *
+         * @param builder the current node builder object
+         * @param currentComponent the name of the current path component
+         * @param components an iterator with all components of the at path
+         * @param orgRoot the original root node of the wrapped configuration
+         */
+        private void prependAtPathComponent(ImmutableNode.Builder builder,
+                String currentComponent, Iterator<String> components,
+                ImmutableNode orgRoot)
+        {
+            builder.name(currentComponent);
+            if (components.hasNext())
+            {
+                ImmutableNode.Builder childBuilder =
+                        new ImmutableNode.Builder();
+                prependAtPathComponent(childBuilder, components.next(),
+                        components, orgRoot);
+                builder.addChild(childBuilder.create());
+            }
+            else
             {
-                // Build the complete path
-                for (String p : atPath)
-                {
-                    ViewNode node = new ViewNode();
-                    node.setName(p);
-                    atParent.addChild(node);
-                    atParent = node;
-                }
+                builder.addChildren(orgRoot.getChildren());
+                builder.addAttributes(orgRoot.getAttributes());
+                builder.value(orgRoot.getValue());
             }
+        }
 
-            // Copy data of the root node to the new path
+        /**
+         * Obtains the root node of the wrapped configuration. If necessary, a
+         * hierarchical representation of the configuration has to be created
+         * first.
+         *
+         * @return the root node of the associated configuration
+         */
+        private ImmutableNode getRootNodeOfConfiguration()
+        {
             getConfiguration().lock(LockMode.READ);
             try
             {
-                ConfigurationNode root =
-                        ConfigurationUtils.convertToHierarchical(
-                                getConfiguration(), conversionExpressionEngine)
-                                .getRootNode();
-                atParent.appendChildren(root);
-                atParent.appendAttributes(root);
+                ImmutableNode root =
+                        ConfigurationUtils
+                                .convertToHierarchical(getConfiguration(),
+                                        conversionExpressionEngine).getNodeModel()
+                                .getInMemoryRepresentation();
                 rootNode = root;
+                return root;
             }
             finally
             {
                 getConfiguration().unlock(LockMode.READ);
             }
-
-            return result;
         }
 
         /**

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DatabaseConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DatabaseConfiguration.java?rev=1588831&r1=1588830&r2=1588831&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DatabaseConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DatabaseConfiguration.java Sun Apr 20 19:32:08 2014
@@ -17,6 +17,7 @@
 
 package org.apache.commons.configuration;
 
+import javax.sql.DataSource;
 import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
@@ -28,8 +29,6 @@ import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 
-import javax.sql.DataSource;
-
 import org.apache.commons.configuration.convert.DisabledListDelimiterHandler;
 import org.apache.commons.configuration.convert.ListDelimiterHandler;
 import org.apache.commons.lang3.StringUtils;
@@ -329,10 +328,9 @@ public class DatabaseConfiguration exten
                 {
                     Object value = extractPropertyValue(rs);
                     // Split value if it contains the list delimiter
-                    Iterator<?> it = getListDelimiterHandler().parse(value);
-                    while (it.hasNext())
+                    for (Object o : getListDelimiterHandler().parse(value))
                     {
-                        results.add(it.next());
+                        results.add(o);
                     }
                 }
 

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=1588831&r1=1588830&r2=1588831&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 Sun Apr 20 19:32:08 2014
@@ -34,8 +34,8 @@ import org.apache.commons.configuration.
 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.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ExpressionEngine;
+import org.apache.commons.configuration.tree.ImmutableNode;
 import org.apache.commons.configuration.tree.NodeCombiner;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -60,9 +60,6 @@ import org.apache.commons.logging.LogFac
  * </p>
  *
  * @since 1.6
- * @author <a
- *         href="http://commons.apache.org/configuration/team-list.html">Commons
- *         Configuration team</a>
  * @version $Id$
  */
 public class DynamicCombinedConfiguration extends CombinedConfiguration
@@ -382,13 +379,13 @@ public class DynamicCombinedConfiguratio
      * @return the combined root node
      */
     @Override
-    public ConfigurationNode getRootNode()
+    public ImmutableNode getRootNode()
     {
         return getCurrentConfig().getRootNode();
     }
 
     @Override
-    protected void setRootNodeInternal(ConfigurationNode rootNode)
+    protected void setRootNodeInternal(ImmutableNode rootNode)
     {
         if (configs != null)
         {
@@ -665,31 +662,31 @@ public class DynamicCombinedConfiguratio
     }
 
     @Override
-    protected void addNodesInternal(String key, Collection<? extends ConfigurationNode> nodes)
+    protected void addNodesInternal(String key, Collection<? extends ImmutableNode> nodes)
     {
         this.getCurrentConfig().addNodes(key, nodes);
     }
 
     @Override
-    public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
+    public HierarchicalConfiguration<ImmutableNode> configurationAt(String key, boolean supportUpdates)
     {
         return this.getCurrentConfig().configurationAt(key, supportUpdates);
     }
 
     @Override
-    public SubnodeConfiguration configurationAt(String key)
+    public HierarchicalConfiguration<ImmutableNode> configurationAt(String key)
     {
         return this.getCurrentConfig().configurationAt(key);
     }
 
     @Override
-    public List<SubnodeConfiguration> configurationsAt(String key)
+    public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(String key)
     {
         return this.getCurrentConfig().configurationsAt(key);
     }
 
     @Override
-    protected List<ConfigurationNode> clearTreeInternal(String key)
+    protected Object clearTreeInternal(String key)
     {
         this.getCurrentConfig().clearTree(key);
         return Collections.emptyList();

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/HierarchicalConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/HierarchicalConfiguration.java?rev=1588831&r1=1588830&r2=1588831&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/HierarchicalConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/HierarchicalConfiguration.java Sun Apr 20 19:32:08 2014
@@ -19,8 +19,8 @@ package org.apache.commons.configuration
 import java.util.Collection;
 import java.util.List;
 
-import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ExpressionEngine;
+import org.apache.commons.configuration.tree.NodeModelSupport;
 
 /**
  * <p>
@@ -28,29 +28,36 @@ import org.apache.commons.configuration.
  * </p>
  * <p>
  * This interface introduces methods for manipulating tree-like structured
- * configuration sources. Also, all methods defined by the
- * {@code Configuration} interface are available.
+ * configuration sources. Also, all methods defined by the {@code Configuration}
+ * interface are available.
+ * </p>
+ * <p>
+ * This interface does not make any assumptions about the concrete type of nodes
+ * used by an implementation; this is reflected by a generic type parameter.
+ * Concrete implementations may therefore define their own hierarchical
+ * structures.
  * </p>
  *
  * @version $Id$
  * @since 2.0
+ * @param <T> the type of the nodes used by this hierarchical configuration
  */
-public interface HierarchicalConfiguration
-    extends Configuration, ImmutableHierarchicalConfiguration
+public interface HierarchicalConfiguration<T>
+    extends Configuration, ImmutableHierarchicalConfiguration, NodeModelSupport<T>
 {
     /**
      * Returns the root node of this hierarchical configuration.
      *
      * @return the root node
      */
-    ConfigurationNode getRootNode();
+    T getRootNode();
 
     /**
      * Sets the root node of this hierarchical configuration.
      *
      * @param rootNode the root node
      */
-    void setRootNode(ConfigurationNode rootNode);
+    void setRootNode(T rootNode);
 
     /**
      * Sets the expression engine to be used by this configuration. All property
@@ -68,12 +75,7 @@ public interface HierarchicalConfigurati
      * instead of a single property a whole collection of nodes can be added -
      * and thus complete configuration sub trees. E.g. with this method it is
      * possible to add parts of another {@code BaseHierarchicalConfiguration}
-     * object to this object. (However be aware that a
-     * {@code ConfigurationNode} object can only belong to a single
-     * configuration. So if nodes from one configuration are directly added to
-     * another one using this method, the structure of the source configuration
-     * will be broken. In this case you should clone the nodes to be added
-     * before calling {@code addNodes()}.) If the passed in key refers to
+     * object to this object. If the passed in key refers to
      * an existing and unique node, the new nodes are added to this node.
      * Otherwise a new node will be created at the specified position in the
      * hierarchy.
@@ -83,11 +85,11 @@ public interface HierarchicalConfigurati
      * @param nodes a collection with the {@code Node} objects to be
      * added
      */
-    void addNodes(String key, Collection<? extends ConfigurationNode> nodes);
+    void addNodes(String key, Collection<? extends T> nodes);
 
     /**
      * <p>
-     * Returns a hierarchical subnode configuration object that wraps the
+     * Returns a hierarchical sub configuration object that wraps the
      * configuration node specified by the given key. This method provides an
      * easy means of accessing sub trees of a hierarchical configuration. In the
      * returned configuration the sub tree can directly be accessed, it becomes
@@ -101,37 +103,32 @@ public interface HierarchicalConfigurati
      * {@code subset()} supports arbitrary subsets of configuration nodes
      * while {@code configurationAt()} only returns a single sub tree.
      * Please refer to the documentation of the
-     * {@code SubnodeConfiguration} class to obtain further information
-     * about subnode configurations and when they should be used.
+     * {@link SubnodeConfiguration} class to obtain further information
+     * about sub configurations and when they should be used.
      * </p>
      * <p>
      * With the {@code supportUpdate} flag the behavior of the returned
-     * {@code SubnodeConfiguration} regarding updates of its parent
-     * configuration can be determined. A subnode configuration operates on the
-     * same nodes as its parent, so changes at one configuration are normally
-     * directly visible for the other configuration. There are however changes
-     * of the parent configuration, which are not recognized by the subnode
-     * configuration per default. An example for this is a reload operation (for
-     * file-based configurations): Here the complete node set of the parent
-     * configuration is replaced, but the subnode configuration still references
-     * the old nodes. If such changes should be detected by the subnode
-     * configuration, the {@code supportUpdates} flag must be set to
-     * <b>true</b>. This causes the subnode configuration to reevaluate the key
-     * used for its creation each time it is accessed. This guarantees that the
-     * subnode configuration always stays in sync with its key, even if the
-     * parent configuration's data significantly changes. If such a change
-     * makes the key invalid - because it now no longer points to exactly one
-     * node -, the subnode configuration is not reconstructed, but keeps its
-     * old data. It is then quasi detached from its parent.
+     * sub configuration regarding updates of its parent
+     * configuration can be determined. If set to <b>false</b>, the configurations
+     * return on independent nodes structures. So changes made on one configuration
+     * cannot be seen by the other one. A value of <b>true</b> in contrast creates
+     * a direct connection between both configurations - they are then using the
+     * same underlying data structures as much as possible. There are however changes
+     * which break this connection; for instance, if the sub tree the sub configuration
+     * belongs to is completely removed from the parent configuration. If such a
+     * change happens, the sub configuration becomes detached from its parent.
+     * It can still be used in a normal way, but changes on it are not reflected
+     * by the parent and vice verse. Also, it is not possible to reattach a once
+     * detached sub configuration.
      * </p>
      *
      * @param key the key that selects the sub tree
-     * @param supportUpdates a flag whether the returned subnode configuration
-     * should be able to handle updates of its parent
+     * @param supportUpdates a flag whether the returned sub configuration
+     * should be directly connected to its parent
      * @return a hierarchical configuration that contains this sub tree
      * @see SubnodeConfiguration
      */
-    SubnodeConfiguration configurationAt(String key, boolean supportUpdates);
+    HierarchicalConfiguration<T> configurationAt(String key, boolean supportUpdates);
 
     /**
      * Returns a hierarchical subnode configuration for the node specified by
@@ -142,17 +139,17 @@ public interface HierarchicalConfigurati
      * @return a hierarchical configuration that contains this sub tree
      * @see SubnodeConfiguration
      */
-    SubnodeConfiguration configurationAt(String key);
+    HierarchicalConfiguration<T> configurationAt(String key);
 
     /**
      * Returns a list of sub configurations for all configuration nodes selected
      * by the given key. This method will evaluate the passed in key (using the
-     * current {@code ExpressionEngine}) and then create a subnode
-     * configuration for each returned node (like
-     * {@link #configurationAt(String)}}). This is especially
-     * useful when dealing with list-like structures. As an example consider the
-     * configuration that contains data about database tables and their fields.
-     * If you need access to all fields of a certain table, you can simply do
+     * current {@code ExpressionEngine}) and then create a sub configuration for
+     * each returned node (like {@link #configurationAt(String)} ). This is
+     * especially useful when dealing with list-like structures. As an example
+     * consider the configuration that contains data about database tables and
+     * their fields. If you need access to all fields of a certain table, you
+     * can simply do
      *
      * <pre>
      * List fields = config.configurationsAt("tables.table(0).fields.field");
@@ -166,23 +163,61 @@ public interface HierarchicalConfigurati
      *     ...
      * </pre>
      *
+     * The configuration objects returned are <strong>not</strong> connected to
+     * the parent configuration.
+     *
+     * @param key the key for selecting the desired nodes
+     * @return a list with hierarchical configuration objects; each
+     *         configuration represents one of the nodes selected by the passed
+     *         in key
+     */
+    List<HierarchicalConfiguration<T>> configurationsAt(String key);
+
+    /**
+     * Returns a list of sub configurations for all configuration nodes selected
+     * by the given key allowing the caller to specify the
+     * {@code supportUpdates} flag. This method works like
+     * {@link #configurationsAt(String)}, but with the additional boolean
+     * parameter it can be specified whether the returned configurations react
+     * on updates of the parent configuration.
+     *
      * @param key the key for selecting the desired nodes
+     * @param supportUpdates a flag whether the returned sub configuration
+     *        should be directly connected to its parent
      * @return a list with hierarchical configuration objects; each
-     * configuration represents one of the nodes selected by the passed in key
+     *         configuration represents one of the nodes selected by the passed
+     *         in key
+     * @see #configurationsAt(String, boolean)
      */
-    List<SubnodeConfiguration> configurationsAt(String key);
+    List<HierarchicalConfiguration<T>> configurationsAt(String key,
+            boolean supportUpdates);
 
     /**
      * Returns a list with sub configurations for all child nodes of the node
      * selected by the given key. This method works like
      * {@link #immutableChildConfigurationsAt(String)}, but returns a list with
-     * {@code SubnodeConfiguration} objects.
+     * mutable configuration objects. The configuration objects returned are
+     * <strong>not</strong> connected to the parent configuration.
      *
      * @param key the key for selecting the desired parent node
-     * @return a collection with {@code SubnodeConfiguration} objects for all
+     * @return a collection with {@code HierarchicalConfiguration} objects for all
      *         child nodes of the selected parent node
      */
-    List<SubnodeConfiguration> childConfigurationsAt(String key);
+    List<HierarchicalConfiguration<T>> childConfigurationsAt(String key);
+
+    /**
+     * Returns a list with sub configurations for all child nodes of the node
+     * selected by the given key allowing the caller to specify the
+     * {@code supportUpdates} flag.
+     *
+     * @param key the key for selecting the desired parent node
+     * @param supportUpdates a flag whether the returned sub configuration
+     *        should be directly connected to its parent
+     * @return a collection with {@code HierarchicalConfiguration} objects for
+     *         all child nodes of the selected parent node
+     */
+    List<HierarchicalConfiguration<T>> childConfigurationsAt(String key,
+            boolean supportUpdates);
 
     /**
      * Removes all values of the property with the given name and of keys that

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/HierarchicalConfigurationXMLReader.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/HierarchicalConfigurationXMLReader.java?rev=1588831&r1=1588830&r2=1588831&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/HierarchicalConfigurationXMLReader.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/HierarchicalConfigurationXMLReader.java Sun Apr 20 19:32:08 2014
@@ -17,31 +17,38 @@
 
 package org.apache.commons.configuration;
 
-import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
+import org.apache.commons.configuration.tree.NodeHandler;
+import org.apache.commons.configuration.tree.NodeTreeWalker;
 import org.xml.sax.Attributes;
 import org.xml.sax.helpers.AttributesImpl;
 
 /**
- * <p>A specialized SAX2 XML parser that "parses" hierarchical
- * configuration objects.</p>
- * <p>This class mimics to be a SAX conform XML parser. Instead of parsing
- * XML documents it processes a {@code Configuration} object and
- * generates SAX events for the single properties defined there. This enables
- * the whole world of XML processing for configuration objects.</p>
- * <p>The {@code HierarchicalConfiguration} object to be parsed can be
- * specified using a constructor or the {@code setConfiguration()} method.
- * This object will be processed by the {@code parse()} methods. Note
- * that these methods ignore their argument.</p>
+ * <p>
+ * A specialized SAX2 XML parser that "parses" hierarchical configuration
+ * objects.
+ * </p>
+ * <p>
+ * This class mimics to be a SAX conform XML parser. Instead of parsing XML
+ * documents it processes a {@code Configuration} object and generates SAX
+ * events for the single properties defined there. This enables the whole world
+ * of XML processing for configuration objects.
+ * </p>
+ * <p>
+ * The {@code HierarchicalConfiguration} object to be parsed can be specified
+ * using a constructor or the {@code setConfiguration()} method. This object
+ * will be processed by the {@code parse()} methods. Note that these methods
+ * ignore their argument.
+ * </p>
  *
- * @author <a
- * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
  * @version $Id$
+ * @param <T> the type of the nodes supported by this reader
  */
-public class HierarchicalConfigurationXMLReader extends ConfigurationXMLReader
+public class HierarchicalConfigurationXMLReader<T> extends
+        ConfigurationXMLReader
 {
-    /** Stores the configuration object to be parsed.*/
-    private HierarchicalConfiguration configuration;
+    /** Stores the configuration object to be parsed. */
+    private HierarchicalConfiguration<T> configuration;
 
     /**
      * Creates a new instance of {@code HierarchicalConfigurationXMLReader}.
@@ -57,7 +64,8 @@ public class HierarchicalConfigurationXM
      *
      * @param config the configuration object
      */
-    public HierarchicalConfigurationXMLReader(HierarchicalConfiguration config)
+    public HierarchicalConfigurationXMLReader(
+            HierarchicalConfiguration<T> config)
     {
         this();
         setConfiguration(config);
@@ -68,7 +76,7 @@ public class HierarchicalConfigurationXM
      *
      * @return the configuration object to be parsed
      */
-    public HierarchicalConfiguration getConfiguration()
+    public HierarchicalConfiguration<T> getConfiguration()
     {
         return configuration;
     }
@@ -78,7 +86,7 @@ public class HierarchicalConfigurationXM
      *
      * @param config the configuration object to be parsed
      */
-    public void setConfiguration(HierarchicalConfiguration config)
+    public void setConfiguration(HierarchicalConfiguration<T> config)
     {
         configuration = config;
     }
@@ -100,50 +108,49 @@ public class HierarchicalConfigurationXM
     @Override
     protected void processKeys()
     {
-        getConfiguration().getRootNode().visit(new SAXVisitor());
+        NodeHandler<T> nodeHandler =
+                getConfiguration().getNodeModel().getNodeHandler();
+        NodeTreeWalker.INSTANCE.walkDFS(nodeHandler.getRootNode(),
+                new SAXVisitor(), nodeHandler);
     }
 
     /**
-     * A specialized visitor class for generating SAX events for a
-     * hierarchical node structure.
-     *
+     * A specialized visitor class for generating SAX events for a hierarchical
+     * node structure.
      */
-    class SAXVisitor extends ConfigurationNodeVisitorAdapter
+    private class SAXVisitor extends ConfigurationNodeVisitorAdapter<T>
     {
-        /** Constant for the attribute type.*/
+        /** Constant for the attribute type. */
         private static final String ATTR_TYPE = "CDATA";
 
         /**
          * Visits the specified node after its children have been processed.
          *
          * @param node the actual node
+         * @param handler the node handler
          */
         @Override
-        public void visitAfterChildren(ConfigurationNode node)
+        public void visitAfterChildren(T node, NodeHandler<T> handler)
         {
-            if (!isAttributeNode(node))
-            {
-                fireElementEnd(nodeName(node));
-            }
+            fireElementEnd(nodeName(node, handler));
         }
 
         /**
          * Visits the specified node.
          *
          * @param node the actual node
-         * @param key the key of this node
+         * @param handler the node handler
          */
         @Override
-        public void visitBeforeChildren(ConfigurationNode node)
+        public void visitBeforeChildren(T node, NodeHandler<T> handler)
         {
-            if (!isAttributeNode(node))
-            {
-                fireElementStart(nodeName(node), fetchAttributes(node));
+            fireElementStart(nodeName(node, handler),
+                    fetchAttributes(node, handler));
 
-                if (node.getValue() != null)
-                {
-                    fireCharacters(node.getValue().toString());
-                }
+            Object value = handler.getValue(node);
+            if (value != null)
+            {
+                fireCharacters(value.toString());
             }
         }
 
@@ -162,19 +169,21 @@ public class HierarchicalConfigurationXM
         /**
          * Returns an object with all attributes for the specified node.
          *
-         * @param node the actual node
+         * @param node the current node
+         * @param handler the node handler
          * @return an object with all attributes of this node
          */
-        protected Attributes fetchAttributes(ConfigurationNode node)
+        protected Attributes fetchAttributes(T node, NodeHandler<T> handler)
         {
             AttributesImpl attrs = new AttributesImpl();
 
-            for (ConfigurationNode child : node.getAttributes())
+            for (String attr : handler.getAttributes(node))
             {
-                if (child.getValue() != null)
+                Object value = handler.getAttributeValue(node, attr);
+                if (value != null)
                 {
-                    String attr = child.getName();
-                    attrs.addAttribute(NS_URI, attr, attr, ATTR_TYPE, child.getValue().toString());
+                    attrs.addAttribute(NS_URI, attr, attr, ATTR_TYPE,
+                            value.toString());
                 }
             }
 
@@ -187,24 +196,13 @@ public class HierarchicalConfigurationXM
          * will be used.
          *
          * @param node the node to be checked
+         * @param handler the node handler
          * @return the name for this node
          */
-        private String nodeName(ConfigurationNode node)
-        {
-            return (node.getName() == null) ? getRootName() : node.getName();
-        }
-
-        /**
-         * Checks if the specified node is an attribute node. In the node
-         * hierarchy attributes are stored as normal child nodes, but with
-         * special names.
-         *
-         * @param node the node to be checked
-         * @return a flag if this is an attribute node
-         */
-        private boolean isAttributeNode(ConfigurationNode node)
+        private String nodeName(T node, NodeHandler<T> handler)
         {
-            return node.isAttribute();
+            String nodeName = handler.nodeName(node);
+            return (nodeName == null) ? getRootName() : nodeName;
         }
     }
 }

Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/INIConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/INIConfiguration.java?rev=1588831&r1=1588830&r2=1588831&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/INIConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/INIConfiguration.java Sun Apr 20 19:32:08 2014
@@ -21,16 +21,25 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.Writer;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.commons.configuration.convert.ListDelimiterHandler;
 import org.apache.commons.configuration.ex.ConfigurationException;
-import org.apache.commons.configuration.tree.ConfigurationNode;
-import org.apache.commons.configuration.tree.ViewNode;
+import org.apache.commons.configuration.ex.ConfigurationRuntimeException;
+import org.apache.commons.configuration.tree.ImmutableNode;
+import org.apache.commons.configuration.tree.InMemoryNodeModel;
+import org.apache.commons.configuration.tree.InMemoryNodeModelSupport;
+import org.apache.commons.configuration.tree.NodeHandler;
+import org.apache.commons.configuration.tree.NodeHandlerDecorator;
+import org.apache.commons.configuration.tree.NodeSelector;
+import org.apache.commons.configuration.tree.TrackedNodeModel;
 
 /**
  * <p>
@@ -255,7 +264,7 @@ public class INIConfiguration extends Ba
      * @param c the configuration to be copied
      * @since 2.0
      */
-    public INIConfiguration(HierarchicalConfiguration c)
+    public INIConfiguration(HierarchicalConfiguration<ImmutableNode> c)
     {
         super(c);
     }
@@ -272,23 +281,16 @@ public class INIConfiguration extends Ba
     public void write(Writer writer) throws ConfigurationException, IOException
     {
         PrintWriter out = new PrintWriter(writer);
-        Iterator<String> it = getSections().iterator();
-        while (it.hasNext())
+        for (String section : getSections())
         {
-            String section = it.next();
-            Configuration subset;
             if (section != null)
             {
                 out.print("[");
                 out.print(section);
                 out.print("]");
                 out.println();
-                subset = createSubnodeConfiguration(getSectionNode(section), null);
-            }
-            else
-            {
-                subset = getSection(null);
             }
+            Configuration subset = getSection(section);
 
             Iterator<String> keys = subset.getKeys();
             while (keys.hasNext())
@@ -327,7 +329,7 @@ public class INIConfiguration extends Ba
      * {@code clear()} method is not called so the configuration read in will
      * be merged with the current configuration.
      *
-     * @param reader The reader to read the configuration from.
+     * @param in the reader to read the configuration from.
      * @throws ConfigurationException If an error occurs while reading the
      *         configuration
      * @throws IOException if an I/O error occurs
@@ -336,9 +338,51 @@ public class INIConfiguration extends Ba
     public void read(Reader in) throws ConfigurationException, IOException
     {
         BufferedReader bufferedReader = new BufferedReader(in);
-        ConfigurationNode sectionNode = getRootNode();
+        Map<String, ImmutableNode.Builder> sectionBuilders = new LinkedHashMap<String, ImmutableNode.Builder>();
+        ImmutableNode.Builder rootBuilder = new ImmutableNode.Builder();
+
+        createNodeBuilders(bufferedReader, rootBuilder, sectionBuilders);
+        ImmutableNode rootNode = createNewRootNode(rootBuilder, sectionBuilders);
+        addNodes(null, rootNode.getChildren());
+    }
+
+    /**
+     * Creates a new root node from the builders constructed while reading the
+     * configuration file.
+     *
+     * @param rootBuilder the builder for the top-level section
+     * @param sectionBuilders a map storing the section builders
+     * @return the root node of the newly created hierarchy
+     */
+    private static ImmutableNode createNewRootNode(
+            ImmutableNode.Builder rootBuilder,
+            Map<String, ImmutableNode.Builder> sectionBuilders)
+    {
+        for (Map.Entry<String, ImmutableNode.Builder> e : sectionBuilders
+                .entrySet())
+        {
+            rootBuilder.addChild(e.getValue().name(e.getKey()).create());
+        }
+        return rootBuilder.create();
+    }
 
-        String line = bufferedReader.readLine();
+    /**
+     * Reads the content of an INI file from the passed in reader and creates a
+     * structure of builders for constructing the {@code ImmutableNode} objects
+     * representing the data.
+     *
+     * @param in the reader
+     * @param rootBuilder the builder for the top-level section
+     * @param sectionBuilders a map storing the section builders
+     * @throws IOException if an I/O error occurs
+     */
+    private void createNodeBuilders(BufferedReader in,
+            ImmutableNode.Builder rootBuilder,
+            Map<String, ImmutableNode.Builder> sectionBuilders)
+            throws IOException
+    {
+        ImmutableNode.Builder sectionBuilder = rootBuilder;
+        String line = in.readLine();
         while (line != null)
         {
             line = line.trim();
@@ -347,20 +391,23 @@ public class INIConfiguration extends Ba
                 if (isSectionLine(line))
                 {
                     String section = line.substring(1, line.length() - 1);
-                    sectionNode = getSectionNode(section);
+                    sectionBuilder = sectionBuilders.get(section);
+                    if (sectionBuilder == null)
+                    {
+                        sectionBuilder = new ImmutableNode.Builder();
+                        sectionBuilders.put(section, sectionBuilder);
+                    }
                 }
 
                 else
                 {
-                    String key = "";
+                    String key;
                     String value = "";
                     int index = findSeparator(line);
                     if (index >= 0)
                     {
                         key = line.substring(0, index);
-                        value =
-                                parseValue(line.substring(index + 1),
-                                        bufferedReader);
+                        value = parseValue(line.substring(index + 1), in);
                     }
                     else
                     {
@@ -372,11 +419,11 @@ public class INIConfiguration extends Ba
                         // use space for properties with no key
                         key = " ";
                     }
-                    createValueNodes(sectionNode, key, value);
+                    createValueNodes(sectionBuilder, key, value);
                 }
             }
 
-            line = bufferedReader.readLine();
+            line = in.readLine();
         }
     }
 
@@ -385,20 +432,20 @@ public class INIConfiguration extends Ba
      * enabled, the value string is split if possible, and for each single value
      * a node is created. Otherwise only a single node is added to the section.
      *
-     * @param sectionNode the section node new nodes have to be added
+     * @param sectionBuilder the section builder for adding new nodes
      * @param key the key
      * @param value the value string
      */
-    private void createValueNodes(ConfigurationNode sectionNode, String key,
-            String value)
+    private void createValueNodes(ImmutableNode.Builder sectionBuilder,
+            String key, String value)
     {
-        Collection<String> values = getListDelimiterHandler().split(value, false);
+        Collection<String> values =
+                getListDelimiterHandler().split(value, false);
 
         for (String v : values)
         {
-            ConfigurationNode node = createNode(key);
-            node.setValue(v);
-            sectionNode.addChild(node);
+            sectionBuilder.addChild(new ImmutableNode.Builder().name(key)
+                    .value(v).create());
         }
     }
 
@@ -737,12 +784,12 @@ public class INIConfiguration extends Ba
         beginRead(false);
         try
         {
-            for (ConfigurationNode node : getRootNode().getChildren())
+            for (ImmutableNode node : getRootNode().getChildren())
             {
                 if (isSectionNode(node))
                 {
                     inSection = true;
-                    sections.add(node.getName());
+                    sections.add(node.getNodeName());
                 }
                 else
                 {
@@ -799,50 +846,20 @@ public class INIConfiguration extends Ba
         {
             try
             {
-                return configurationAt(name);
+                return (SubnodeConfiguration) configurationAt(name, true);
             }
-            catch (IllegalArgumentException iex)
+            catch (ConfigurationRuntimeException iex)
             {
                 // the passed in key does not map to exactly one node
                 // obtain the node for the section, create it on demand
-                // (creation of a SubnodeConfiguration has to be synchronized)
-                beginWrite(false);
-                try
-                {
-                    return createAndInitializeSubnodeConfiguration(
-                            getSectionNode(name), null, false);
-                }
-                finally
-                {
-                    endWrite();
-                }
+                InMemoryNodeModel parentModel = getSubConfigurationParentModel();
+                NodeSelector selector = parentModel.trackChildNodeWithCreation(null, name, this);
+                return createSubConfigurationForTrackedNode(selector, this);
             }
         }
     }
 
     /**
-     * Obtains the node representing the specified section. This method is
-     * called while the configuration is loaded. If a node for this section
-     * already exists, it is returned. Otherwise a new node is created.
-     *
-     * @param sectionName the name of the section
-     * @return the node for this section
-     */
-    private ConfigurationNode getSectionNode(String sectionName)
-    {
-        List<ConfigurationNode> nodes = getRootNode().getChildren(sectionName);
-        if (!nodes.isEmpty())
-        {
-            return nodes.get(0);
-        }
-
-        ConfigurationNode node = createNode(sectionName);
-        markSectionNode(node);
-        getRootNode().addChild(node);
-        return node;
-    }
-
-    /**
      * Creates a sub configuration for the global section of the represented INI
      * configuration.
      *
@@ -850,47 +867,136 @@ public class INIConfiguration extends Ba
      */
     private SubnodeConfiguration getGlobalSection()
     {
-        ViewNode parent = new ViewNode();
-
-        beginWrite(false);
-        try
-        {
-            for (ConfigurationNode node : getRootNode().getChildren())
-            {
-                if (!isSectionNode(node))
-                {
-                    parent.addChild(node);
-                }
-            }
-
-            return createAndInitializeSubnodeConfiguration(parent, null, false);
-        }
-        finally
-        {
-            endWrite();
-        }
+        InMemoryNodeModel parentModel = getSubConfigurationParentModel();
+        NodeSelector selector = new NodeSelector(null); // selects parent
+        parentModel.trackNode(selector, this);
+        GlobalSectionNodeModel model =
+                new GlobalSectionNodeModel(this, selector);
+        SubnodeConfiguration sub = new SubnodeConfiguration(this, model);
+        initSubConfigurationForThisParent(sub);
+        return sub;
     }
 
     /**
-     * Marks a configuration node as a section node. This means that this node
-     * represents a section header. This implementation uses the node's
-     * reference property to store a flag.
+     * Checks whether the specified configuration node represents a section.
      *
-     * @param node the node to be marked
+     * @param node the node in question
+     * @return a flag whether this node represents a section
      */
-    private static void markSectionNode(ConfigurationNode node)
+    private static boolean isSectionNode(ImmutableNode node)
     {
-        node.setReference(Boolean.TRUE);
+        return !node.getChildren().isEmpty();
     }
 
     /**
-     * Checks whether the specified configuration node represents a section.
-     *
-     * @param node the node in question
-     * @return a flag whether this node represents a section
+     * A specialized node model implementation for the sub configuration
+     * representing the global section of the INI file. This is a regular
+     * {@code TrackedNodeModel} with one exception: The {@code NodeHandler} used
+     * by this model applies a filter on the children of the root node so that
+     * only nodes are visible that are no sub sections.
      */
-    private static boolean isSectionNode(ConfigurationNode node)
+    private static class GlobalSectionNodeModel extends TrackedNodeModel
     {
-        return node.getReference() != null || node.getChildrenCount() > 0;
+        /**
+         * Creates a new instance of {@code GlobalSectionNodeModel} and
+         * initializes it with the given underlying model.
+         *
+         * @param modelSupport the underlying {@code InMemoryNodeModel}
+         * @param selector the {@code NodeSelector}
+         */
+        public GlobalSectionNodeModel(InMemoryNodeModelSupport modelSupport,
+                NodeSelector selector)
+        {
+            super(modelSupport, selector, true);
+        }
+
+        @Override
+        public NodeHandler<ImmutableNode> getNodeHandler()
+        {
+            return new NodeHandlerDecorator<ImmutableNode>()
+            {
+                @Override
+                public List<ImmutableNode> getChildren(ImmutableNode node)
+                {
+                    List<ImmutableNode> children = super.getChildren(node);
+                    return filterChildrenOfGlobalSection(node, children);
+                }
+
+                @Override
+                public List<ImmutableNode> getChildren(ImmutableNode node,
+                        String name)
+                {
+                    List<ImmutableNode> children =
+                            super.getChildren(node, name);
+                    return filterChildrenOfGlobalSection(node, children);
+                }
+
+                @Override
+                public int getChildrenCount(ImmutableNode node, String name)
+                {
+                    List<ImmutableNode> children =
+                            (name != null) ? super.getChildren(node, name)
+                                    : super.getChildren(node);
+                    return filterChildrenOfGlobalSection(node, children).size();
+                }
+
+                @Override
+                public ImmutableNode getChild(ImmutableNode node, int index)
+                {
+                    List<ImmutableNode> children = super.getChildren(node);
+                    return filterChildrenOfGlobalSection(node, children).get(
+                            index);
+                }
+
+                @Override
+                public int indexOfChild(ImmutableNode parent,
+                        ImmutableNode child)
+                {
+                    List<ImmutableNode> children = super.getChildren(parent);
+                    return filterChildrenOfGlobalSection(parent, children)
+                            .indexOf(child);
+                }
+
+                @Override
+                protected NodeHandler<ImmutableNode> getDecoratedNodeHandler()
+                {
+                    return GlobalSectionNodeModel.super.getNodeHandler();
+                }
+
+                /**
+                 * Filters the child nodes of the global section. This method
+                 * checks whether the passed in node is the root node of the
+                 * configuration. If so, from the list of children all nodes are
+                 * filtered which are section nodes.
+                 *
+                 * @param node the node in question
+                 * @param children the children of this node
+                 * @return a list with the filtered children
+                 */
+                private List<ImmutableNode> filterChildrenOfGlobalSection(
+                        ImmutableNode node, List<ImmutableNode> children)
+                {
+                    List<ImmutableNode> filteredList;
+                    if (node == getRootNode())
+                    {
+                        filteredList =
+                                new ArrayList<ImmutableNode>(children.size());
+                        for (ImmutableNode child : children)
+                        {
+                            if (!isSectionNode(child))
+                            {
+                                filteredList.add(child);
+                            }
+                        }
+                    }
+                    else
+                    {
+                        filteredList = children;
+                    }
+
+                    return filteredList;
+                }
+            };
+        }
     }
 }

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=1588831&r1=1588830&r2=1588831&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 Sun Apr 20 19:32:08 2014
@@ -31,8 +31,8 @@ import org.apache.commons.configuration.
 import org.apache.commons.configuration.event.ConfigurationListener;
 import org.apache.commons.configuration.ex.ConfigurationException;
 import org.apache.commons.configuration.io.FileBased;
-import org.apache.commons.configuration.tree.ConfigurationNode;
 import org.apache.commons.configuration.tree.ExpressionEngine;
+import org.apache.commons.configuration.tree.ImmutableNode;
 
 /**
  * Wraps a BaseHierarchicalConfiguration and allows subtrees to be accessed via a configured path with
@@ -50,7 +50,7 @@ public class PatternSubtreeConfiguration
     private static final long serialVersionUID = 7456061169617014189L;
 
     /** The wrapped configuration */
-    private final HierarchicalConfiguration config;
+    private final HierarchicalConfiguration<ImmutableNode> config;
 
     /** The path to the subtree */
     private final String path;
@@ -66,7 +66,8 @@ public class PatternSubtreeConfiguration
      * @param config The Configuration to be wrapped.
      * @param path The base path pattern.
      */
-    public PatternSubtreeConfigurationWrapper(HierarchicalConfiguration config, String path)
+    public PatternSubtreeConfigurationWrapper(
+            HierarchicalConfiguration<ImmutableNode> config, String path)
     {
         this.config = config;
         this.path = path;
@@ -321,13 +322,13 @@ public class PatternSubtreeConfiguration
     }
 
     @Override
-    public ConfigurationNode getRootNode()
+    public ImmutableNode getRootNode()
     {
         return getConfig().getRootNode();
     }
 
     @Override
-    protected void setRootNodeInternal(ConfigurationNode rootNode)
+    protected void setRootNodeInternal(ImmutableNode rootNode)
     {
         if (init)
         {
@@ -359,31 +360,31 @@ public class PatternSubtreeConfiguration
     }
 
     @Override
-    protected void addNodesInternal(String key, Collection<? extends ConfigurationNode> nodes)
+    protected void addNodesInternal(String key, Collection<? extends ImmutableNode> nodes)
     {
         getConfig().addNodes(key, nodes);
     }
 
     @Override
-    public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
+    public HierarchicalConfiguration<ImmutableNode> configurationAt(String key, boolean supportUpdates)
     {
         return config.configurationAt(makePath(key), supportUpdates);
     }
 
     @Override
-    public SubnodeConfiguration configurationAt(String key)
+    public HierarchicalConfiguration<ImmutableNode> configurationAt(String key)
     {
         return config.configurationAt(makePath(key));
     }
 
     @Override
-    public List<SubnodeConfiguration> configurationsAt(String key)
+    public List<HierarchicalConfiguration<ImmutableNode>> configurationsAt(String key)
     {
         return config.configurationsAt(makePath(key));
     }
 
     @Override
-    protected List<ConfigurationNode> clearTreeInternal(String key)
+    protected Object clearTreeInternal(String key)
     {
         config.clearTree(makePath(key));
         return Collections.emptyList();
@@ -463,7 +464,7 @@ public class PatternSubtreeConfiguration
 
     private BaseHierarchicalConfiguration getConfig()
     {
-        return config.configurationAt(makePath());
+        return (BaseHierarchicalConfiguration) config.configurationAt(makePath());
     }
 
     private String makePath()