Coverage Report - org.apache.commons.configuration.HierarchicalConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
HierarchicalConfiguration
94%
204/216
98%
100/102
2,111
HierarchicalConfiguration$1
100%
2/2
N/A
2,111
HierarchicalConfiguration$2
100%
3/3
N/A
2,111
HierarchicalConfiguration$3
100%
3/3
N/A
2,111
HierarchicalConfiguration$4
100%
3/3
N/A
2,111
HierarchicalConfiguration$5
100%
4/4
N/A
2,111
HierarchicalConfiguration$BuilderVisitor
100%
30/30
100%
20/20
2,111
HierarchicalConfiguration$CloneVisitor
100%
16/16
100%
6/6
2,111
HierarchicalConfiguration$DefinedKeysVisitor
100%
16/16
100%
4/4
2,111
HierarchicalConfiguration$DefinedVisitor
100%
5/5
100%
2/2
2,111
HierarchicalConfiguration$Node
95%
47/49
83%
25/30
2,111
HierarchicalConfiguration$NodeVisitor
100%
4/4
N/A
2,111
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.configuration;
 19  
 
 20  
 import java.io.Serializable;
 21  
 import java.util.ArrayList;
 22  
 import java.util.Collection;
 23  
 import java.util.Collections;
 24  
 import java.util.Iterator;
 25  
 import java.util.LinkedHashSet;
 26  
 import java.util.LinkedList;
 27  
 import java.util.List;
 28  
 import java.util.Set;
 29  
 import java.util.Stack;
 30  
 
 31  
 import org.apache.commons.configuration.event.ConfigurationEvent;
 32  
 import org.apache.commons.configuration.event.ConfigurationListener;
 33  
 import org.apache.commons.configuration.tree.ConfigurationNode;
 34  
 import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
 35  
 import org.apache.commons.configuration.tree.DefaultConfigurationNode;
 36  
 import org.apache.commons.configuration.tree.DefaultExpressionEngine;
 37  
 import org.apache.commons.configuration.tree.ExpressionEngine;
 38  
 import org.apache.commons.configuration.tree.NodeAddData;
 39  
 import org.apache.commons.configuration.tree.ViewNode;
 40  
 import org.apache.commons.lang.StringUtils;
 41  
 
 42  
 /**
 43  
  * <p>A specialized configuration class that extends its base class by the
 44  
  * ability of keeping more structure in the stored properties.</p><p>There
 45  
  * are some sources of configuration data that cannot be stored very well in a
 46  
  * {@code BaseConfiguration} object because then their structure is lost.
 47  
  * This is especially true for XML documents. This class can deal with such
 48  
  * structured configuration sources by storing the properties in a tree-like
 49  
  * organization.</p><p>The internal used storage form allows for a more
 50  
  * sophisticated access to single properties. As an example consider the
 51  
  * following XML document:</p><p>
 52  
  *
 53  
  * <pre>
 54  
  * &lt;database&gt;
 55  
  *   &lt;tables&gt;
 56  
  *     &lt;table&gt;
 57  
  *       &lt;name&gt;users&lt;/name&gt;
 58  
  *       &lt;fields&gt;
 59  
  *         &lt;field&gt;
 60  
  *           &lt;name&gt;lid&lt;/name&gt;
 61  
  *           &lt;type&gt;long&lt;/name&gt;
 62  
  *         &lt;/field&gt;
 63  
  *         &lt;field&gt;
 64  
  *           &lt;name&gt;usrName&lt;/name&gt;
 65  
  *           &lt;type&gt;java.lang.String&lt;/type&gt;
 66  
  *         &lt;/field&gt;
 67  
  *        ...
 68  
  *       &lt;/fields&gt;
 69  
  *     &lt;/table&gt;
 70  
  *     &lt;table&gt;
 71  
  *       &lt;name&gt;documents&lt;/name&gt;
 72  
  *       &lt;fields&gt;
 73  
  *         &lt;field&gt;
 74  
  *           &lt;name&gt;docid&lt;/name&gt;
 75  
  *           &lt;type&gt;long&lt;/type&gt;
 76  
  *         &lt;/field&gt;
 77  
  *         ...
 78  
  *       &lt;/fields&gt;
 79  
  *     &lt;/table&gt;
 80  
  *     ...
 81  
  *   &lt;/tables&gt;
 82  
  * &lt;/database&gt;
 83  
  * </pre>
 84  
  *
 85  
  * </p><p>If this document is parsed and stored in a
 86  
  * {@code HierarchicalConfiguration} object (which can be done by one of
 87  
  * the sub classes), there are enhanced possibilities of accessing properties.
 88  
  * The keys for querying information can contain indices that select a certain
 89  
  * element if there are multiple hits.</p><p>For instance the key
 90  
  * {@code tables.table(0).name} can be used to find out the name of the
 91  
  * first table. In opposite {@code tables.table.name} would return a
 92  
  * collection with the names of all available tables. Similarly the key
 93  
  * {@code tables.table(1).fields.field.name} returns a collection with
 94  
  * the names of all fields of the second table. If another index is added after
 95  
  * the {@code field} element, a single field can be accessed:
 96  
  * {@code tables.table(1).fields.field(0).name}.</p><p>There is a
 97  
  * {@code getMaxIndex()} method that returns the maximum allowed index
 98  
  * that can be added to a given property key. This method can be used to iterate
 99  
  * over all values defined for a certain property.</p>
 100  
  * <p>Since the 1.3 release of <em>Commons Configuration</em> hierarchical
 101  
  * configurations support an <em>expression engine</em>. This expression engine
 102  
  * is responsible for evaluating the passed in configuration keys and map them
 103  
  * to the stored properties. The examples above are valid for the default
 104  
  * expression engine, which is used when a new {@code HierarchicalConfiguration}
 105  
  * instance is created. With the {@code setExpressionEngine()} method a
 106  
  * different expression engine can be set. For instance with
 107  
  * {@link org.apache.commons.configuration.tree.xpath.XPathExpressionEngine}
 108  
  * there is an expression engine available that supports configuration keys in
 109  
  * XPATH syntax.</p>
 110  
  * <p>In addition to the events common for all configuration classes hierarchical
 111  
  * configurations support some more events that correspond to some specific
 112  
  * methods and features:
 113  
  * <dl><dt><em>EVENT_ADD_NODES</em></dt><dd>The {@code addNodes()} method
 114  
  * was called; the event object contains the key, to which the nodes were added,
 115  
  * and a collection with the new nodes as value.</dd>
 116  
  * <dt><em>EVENT_CLEAR_TREE</em></dt><dd>The {@code clearTree()} method was
 117  
  * called; the event object stores the key of the removed sub tree.</dd>
 118  
  * <dt><em>EVENT_SUBNODE_CHANGED</em></dt><dd>A {@code SubnodeConfiguration}
 119  
  * that was created from this configuration has been changed. The value property
 120  
  * of the event object contains the original event object as it was sent by the
 121  
  * subnode configuration.</dd></dl></p>
 122  
  * <p><em>Note:</em>Configuration objects of this type can be read concurrently
 123  
  * by multiple threads. However if one of these threads modifies the object,
 124  
  * synchronization has to be performed manually.</p>
 125  
  *
 126  
  * @author Oliver Heger
 127  
  * @version $Id: HierarchicalConfiguration.java 1330666 2012-04-26 06:12:30Z oheger $
 128  
  */
 129  9167
 public class HierarchicalConfiguration extends AbstractConfiguration implements Serializable, Cloneable
 130  
 {
 131  
     /**
 132  
      * Constant for the clear tree event.
 133  
      * @since 1.3
 134  
      */
 135  
     public static final int EVENT_CLEAR_TREE = 10;
 136  
 
 137  
     /**
 138  
      * Constant for the add nodes event.
 139  
      * @since 1.3
 140  
      */
 141  
     public static final int EVENT_ADD_NODES = 11;
 142  
 
 143  
     /**
 144  
      * Constant for the subnode configuration modified event.
 145  
      * @since 1.5
 146  
      */
 147  
     public static final int EVENT_SUBNODE_CHANGED = 12;
 148  
 
 149  
     /**
 150  
      * The serial version UID.
 151  
      */
 152  
     private static final long serialVersionUID = 3373812230395363192L;
 153  
 
 154  
     /** Stores the default expression engine to be used for new objects.*/
 155  
     private static ExpressionEngine defaultExpressionEngine;
 156  
 
 157  
     /** Stores the root node of this configuration. This field is required for
 158  
      * backwards compatibility only.
 159  
      */
 160  
     private Node root;
 161  
 
 162  
     /** Stores the root configuration node.*/
 163  
     private ConfigurationNode rootNode;
 164  
 
 165  
     /** Stores the expression engine for this instance.*/
 166  
     private transient ExpressionEngine expressionEngine;
 167  
 
 168  
     /**
 169  
      * Creates a new instance of {@code HierarchicalConfiguration}.
 170  
      */
 171  
     public HierarchicalConfiguration()
 172  12481
     {
 173  12484
         setRootNode(new Node());
 174  12481
     }
 175  
 
 176  
     /**
 177  
      * Creates a new instance of {@code HierarchicalConfiguration} and
 178  
      * copies all data contained in the specified configuration into the new
 179  
      * one.
 180  
      *
 181  
      * @param c the configuration that is to be copied (if <b>null</b>, this
 182  
      * constructor will behave like the standard constructor)
 183  
      * @since 1.4
 184  
      */
 185  
     public HierarchicalConfiguration(HierarchicalConfiguration c)
 186  
     {
 187  7053
         this();
 188  7054
         if (c != null)
 189  
         {
 190  7053
             CloneVisitor visitor = new CloneVisitor();
 191  7052
             c.getRootNode().visit(visitor);
 192  7053
             setRootNode(visitor.getClone());
 193  
         }
 194  7054
     }
 195  
 
 196  
     /**
 197  
      * Returns the object to synchronize on a reload. This class is not
 198  
      * reloadable so this object isn't important
 199  
      *
 200  
      * @return the lock object
 201  
      */
 202  
     public Object getReloadLock()
 203  
     {
 204  0
         return this;
 205  
     }
 206  
 
 207  
     /**
 208  
      * Returns the root node of this hierarchical configuration. This method
 209  
      * exists for backwards compatibility only. New code should use the
 210  
      * {@link #getRootNode()} method instead, which operates on
 211  
      * the preferred data type {@code ConfigurationNode}.
 212  
      *
 213  
      * @return the root node
 214  
      */
 215  
     public Node getRoot()
 216  
     {
 217  30278
         if (root == null && rootNode != null)
 218  
         {
 219  
             // Dynamically create a snapshot of the root node
 220  7
             return new Node(rootNode);
 221  
         }
 222  
 
 223  30383
         return root;
 224  
     }
 225  
 
 226  
     /**
 227  
      * Sets the root node of this hierarchical configuration. This method
 228  
      * exists for backwards compatibility only. New code should use the
 229  
      * {@link #setRootNode(ConfigurationNode)} method instead,
 230  
      * which operates on the preferred data type {@code ConfigurationNode}.
 231  
      *
 232  
      * @param node the root node
 233  
      */
 234  
     public void setRoot(Node node)
 235  
     {
 236  12507
         if (node == null)
 237  
         {
 238  1
             throw new IllegalArgumentException("Root node must not be null!");
 239  
         }
 240  12503
         root = node;
 241  12486
         rootNode = null;
 242  12530
     }
 243  
 
 244  
     /**
 245  
      * Returns the root node of this hierarchical configuration.
 246  
      *
 247  
      * @return the root node
 248  
      * @since 1.3
 249  
      */
 250  
     public ConfigurationNode getRootNode()
 251  
     {
 252  508836
         return (rootNode != null) ? rootNode : root;
 253  
     }
 254  
 
 255  
     /**
 256  
      * Sets the root node of this hierarchical configuration.
 257  
      *
 258  
      * @param rootNode the root node
 259  
      * @since 1.3
 260  
      */
 261  
     public void setRootNode(ConfigurationNode rootNode)
 262  
     {
 263  23353
         if (rootNode == null)
 264  
         {
 265  1
             throw new IllegalArgumentException("Root node must not be null!");
 266  
         }
 267  23352
         this.rootNode = rootNode;
 268  
 
 269  
         // For backward compatibility also set the old root field.
 270  23354
         root = (rootNode instanceof Node) ? (Node) rootNode : null;
 271  23352
     }
 272  
 
 273  
     /**
 274  
      * Returns the default expression engine.
 275  
      *
 276  
      * @return the default expression engine
 277  
      * @since 1.3
 278  
      */
 279  
     public static synchronized ExpressionEngine getDefaultExpressionEngine()
 280  
     {
 281  240877
         if (defaultExpressionEngine == null)
 282  
         {
 283  1
             defaultExpressionEngine = new DefaultExpressionEngine();
 284  
         }
 285  240877
         return defaultExpressionEngine;
 286  
     }
 287  
 
 288  
     /**
 289  
      * Sets the default expression engine. This expression engine will be used
 290  
      * if no specific engine was set for an instance. It is shared between all
 291  
      * hierarchical configuration instances. So modifying its properties will
 292  
      * impact all instances, for which no specific engine is set.
 293  
      *
 294  
      * @param engine the new default expression engine
 295  
      * @since 1.3
 296  
      */
 297  
     public static synchronized void setDefaultExpressionEngine(ExpressionEngine engine)
 298  
     {
 299  3
         if (engine == null)
 300  
         {
 301  1
             throw new IllegalArgumentException(
 302  
                     "Default expression engine must not be null!");
 303  
         }
 304  2
         defaultExpressionEngine = engine;
 305  2
     }
 306  
 
 307  
     /**
 308  
      * Returns the expression engine used by this configuration. This method
 309  
      * will never return <b>null</b>; if no specific expression engine was set,
 310  
      * the default expression engine will be returned.
 311  
      *
 312  
      * @return the current expression engine
 313  
      * @since 1.3
 314  
      */
 315  
     public ExpressionEngine getExpressionEngine()
 316  
     {
 317  658714
         return (expressionEngine != null) ? expressionEngine
 318  
                 : getDefaultExpressionEngine();
 319  
     }
 320  
 
 321  
     /**
 322  
      * Sets the expression engine to be used by this configuration. All property
 323  
      * keys this configuration has to deal with will be interpreted by this
 324  
      * engine.
 325  
      *
 326  
      * @param expressionEngine the new expression engine; can be <b>null</b>,
 327  
      * then the default expression engine will be used
 328  
      * @since 1.3
 329  
      */
 330  
     public void setExpressionEngine(ExpressionEngine expressionEngine)
 331  
     {
 332  4657
         this.expressionEngine = expressionEngine;
 333  4657
     }
 334  
 
 335  
     /**
 336  
      * Fetches the specified property. This task is delegated to the associated
 337  
      * expression engine.
 338  
      *
 339  
      * @param key the key to be looked up
 340  
      * @return the found value
 341  
      */
 342  
     public Object getProperty(String key)
 343  
     {
 344  638874
         List<ConfigurationNode> nodes = fetchNodeList(key);
 345  
 
 346  638601
         if (nodes.size() == 0)
 347  
         {
 348  422713
             return null;
 349  
         }
 350  
         else
 351  
         {
 352  216184
             List<Object> list = new ArrayList<Object>();
 353  216203
             for (ConfigurationNode node : nodes)
 354  
             {
 355  218897
                 if (node.getValue() != null)
 356  
                 {
 357  218623
                     list.add(node.getValue());
 358  
                 }
 359  218869
             }
 360  
 
 361  216208
             if (list.size() < 1)
 362  
             {
 363  37
                 return null;
 364  
             }
 365  
             else
 366  
             {
 367  216188
                 return (list.size() == 1) ? list.get(0) : list;
 368  
             }
 369  
         }
 370  
     }
 371  
 
 372  
     /**
 373  
      * Adds the property with the specified key. This task will be delegated to
 374  
      * the associated {@code ExpressionEngine}, so the passed in key
 375  
      * must match the requirements of this implementation.
 376  
      *
 377  
      * @param key the key of the new property
 378  
      * @param obj the value of the new property
 379  
      */
 380  
     @Override
 381  
     protected void addPropertyDirect(String key, Object obj)
 382  
     {
 383  10151
         NodeAddData data = getExpressionEngine().prepareAdd(getRootNode(), key);
 384  10150
         ConfigurationNode node = processNodeAddData(data);
 385  10150
         node.setValue(obj);
 386  10150
     }
 387  
 
 388  
     /**
 389  
      * Adds a collection of nodes at the specified position of the configuration
 390  
      * tree. This method works similar to {@code addProperty()}, but
 391  
      * instead of a single property a whole collection of nodes can be added -
 392  
      * and thus complete configuration sub trees. E.g. with this method it is
 393  
      * possible to add parts of another {@code HierarchicalConfiguration}
 394  
      * object to this object. (However be aware that a
 395  
      * {@code ConfigurationNode} object can only belong to a single
 396  
      * configuration. So if nodes from one configuration are directly added to
 397  
      * another one using this method, the structure of the source configuration
 398  
      * will be broken. In this case you should clone the nodes to be added
 399  
      * before calling {@code addNodes()}.) If the passed in key refers to
 400  
      * an existing and unique node, the new nodes are added to this node.
 401  
      * Otherwise a new node will be created at the specified position in the
 402  
      * hierarchy.
 403  
      *
 404  
      * @param key the key where the nodes are to be added; can be <b>null </b>,
 405  
      * then they are added to the root node
 406  
      * @param nodes a collection with the {@code Node} objects to be
 407  
      * added
 408  
      */
 409  
     public void addNodes(String key, Collection<? extends ConfigurationNode> nodes)
 410  
     {
 411  32
         if (nodes == null || nodes.isEmpty())
 412  
         {
 413  4
             return;
 414  
         }
 415  
 
 416  28
         fireEvent(EVENT_ADD_NODES, key, nodes, true);
 417  
         ConfigurationNode parent;
 418  28
         List<ConfigurationNode> target = fetchNodeList(key);
 419  28
         if (target.size() == 1)
 420  
         {
 421  
             // existing unique key
 422  15
             parent = target.get(0);
 423  
         }
 424  
         else
 425  
         {
 426  
             // otherwise perform an add operation
 427  13
             parent = processNodeAddData(getExpressionEngine().prepareAdd(
 428  
                     getRootNode(), key));
 429  
         }
 430  
 
 431  28
         if (parent.isAttribute())
 432  
         {
 433  1
             throw new IllegalArgumentException(
 434  
                     "Cannot add nodes to an attribute node!");
 435  
         }
 436  
 
 437  27
         for (ConfigurationNode child : nodes)
 438  
         {
 439  63
             if (child.isAttribute())
 440  
             {
 441  3
                 parent.addAttribute(child);
 442  
             }
 443  
             else
 444  
             {
 445  60
                 parent.addChild(child);
 446  
             }
 447  63
             clearReferences(child);
 448  63
         }
 449  27
         fireEvent(EVENT_ADD_NODES, key, nodes, false);
 450  27
     }
 451  
 
 452  
     /**
 453  
      * Checks if this configuration is empty. Empty means that there are no keys
 454  
      * with any values, though there can be some (empty) nodes.
 455  
      *
 456  
      * @return a flag if this configuration is empty
 457  
      */
 458  
     public boolean isEmpty()
 459  
     {
 460  67
         return !nodeDefined(getRootNode());
 461  
     }
 462  
 
 463  
     /**
 464  
      * Creates a new {@code Configuration} object containing all keys
 465  
      * that start with the specified prefix. This implementation will return a
 466  
      * {@code HierarchicalConfiguration} object so that the structure of
 467  
      * the keys will be saved. The nodes selected by the prefix (it is possible
 468  
      * that multiple nodes are selected) are mapped to the root node of the
 469  
      * returned configuration, i.e. their children and attributes will become
 470  
      * children and attributes of the new root node. However a value of the root
 471  
      * node is only set if exactly one of the selected nodes contain a value (if
 472  
      * multiple nodes have a value, there is simply no way to decide how these
 473  
      * values are merged together). Note that the returned
 474  
      * {@code Configuration} object is not connected to its source
 475  
      * configuration: updates on the source configuration are not reflected in
 476  
      * the subset and vice versa.
 477  
      *
 478  
      * @param prefix the prefix of the keys for the subset
 479  
      * @return a new configuration object representing the selected subset
 480  
      */
 481  
     @Override
 482  
     public Configuration subset(String prefix)
 483  
     {
 484  29
         Collection<ConfigurationNode> nodes = fetchNodeList(prefix);
 485  29
         if (nodes.isEmpty())
 486  
         {
 487  2
             return new HierarchicalConfiguration();
 488  
         }
 489  
 
 490  27
         final HierarchicalConfiguration parent = this;
 491  27
         HierarchicalConfiguration result = new HierarchicalConfiguration()
 492  27
         {
 493  
             // Override interpolate to always interpolate on the parent
 494  
             @Override
 495  
             protected Object interpolate(Object value)
 496  
             {
 497  17
                 return parent.interpolate(value);
 498  
             }
 499  
         };
 500  27
         CloneVisitor visitor = new CloneVisitor();
 501  
 
 502  
         // Initialize the new root node
 503  27
         Object value = null;
 504  27
         int valueCount = 0;
 505  27
         for (ConfigurationNode nd : nodes)
 506  
         {
 507  53
             if (nd.getValue() != null)
 508  
             {
 509  14
                 value = nd.getValue();
 510  14
                 valueCount++;
 511  
             }
 512  53
             nd.visit(visitor);
 513  
 
 514  53
             for (ConfigurationNode c : visitor.getClone().getChildren())
 515  
             {
 516  118
                 result.getRootNode().addChild(c);
 517  118
             }
 518  53
             for (ConfigurationNode attr : visitor.getClone().getAttributes())
 519  
             {
 520  2
                 result.getRootNode().addAttribute(attr);
 521  2
             }
 522  53
         }
 523  
 
 524  
         // Determine the value of the new root
 525  27
         if (valueCount == 1)
 526  
         {
 527  2
             result.getRootNode().setValue(value);
 528  
         }
 529  27
         return (result.isEmpty()) ? new HierarchicalConfiguration() : result;
 530  
     }
 531  
 
 532  
     /**
 533  
      * <p>
 534  
      * Returns a hierarchical subnode configuration object that wraps the
 535  
      * configuration node specified by the given key. This method provides an
 536  
      * easy means of accessing sub trees of a hierarchical configuration. In the
 537  
      * returned configuration the sub tree can directly be accessed, it becomes
 538  
      * the root node of this configuration. Because of this the passed in key
 539  
      * must select exactly one configuration node; otherwise an
 540  
      * {@code IllegalArgumentException} will be thrown.
 541  
      * </p>
 542  
      * <p>
 543  
      * The difference between this method and the
 544  
      * {@link #subset(String)} method is that
 545  
      * {@code subset()} supports arbitrary subsets of configuration nodes
 546  
      * while {@code configurationAt()} only returns a single sub tree.
 547  
      * Please refer to the documentation of the
 548  
      * {@code SubnodeConfiguration} class to obtain further information
 549  
      * about subnode configurations and when they should be used.
 550  
      * </p>
 551  
      * <p>
 552  
      * With the {@code supportUpdate} flag the behavior of the returned
 553  
      * {@code SubnodeConfiguration} regarding updates of its parent
 554  
      * configuration can be determined. A subnode configuration operates on the
 555  
      * same nodes as its parent, so changes at one configuration are normally
 556  
      * directly visible for the other configuration. There are however changes
 557  
      * of the parent configuration, which are not recognized by the subnode
 558  
      * configuration per default. An example for this is a reload operation (for
 559  
      * file-based configurations): Here the complete node set of the parent
 560  
      * configuration is replaced, but the subnode configuration still references
 561  
      * the old nodes. If such changes should be detected by the subnode
 562  
      * configuration, the {@code supportUpdates} flag must be set to
 563  
      * <b>true</b>. This causes the subnode configuration to reevaluate the key
 564  
      * used for its creation each time it is accessed. This guarantees that the
 565  
      * subnode configuration always stays in sync with its key, even if the
 566  
      * parent configuration's data significantly changes. If such a change
 567  
      * makes the key invalid - because it now no longer points to exactly one
 568  
      * node -, the subnode configuration is not reconstructed, but keeps its
 569  
      * old data. It is then quasi detached from its parent.
 570  
      * </p>
 571  
      *
 572  
      * @param key the key that selects the sub tree
 573  
      * @param supportUpdates a flag whether the returned subnode configuration
 574  
      * should be able to handle updates of its parent
 575  
      * @return a hierarchical configuration that contains this sub tree
 576  
      * @see SubnodeConfiguration
 577  
      * @since 1.5
 578  
      */
 579  
     public SubnodeConfiguration configurationAt(String key,
 580  
             boolean supportUpdates)
 581  
     {
 582  551
         List<ConfigurationNode> nodes = fetchNodeList(key);
 583  551
         if (nodes.size() != 1)
 584  
         {
 585  48
             throw new IllegalArgumentException(
 586  
                     "Passed in key must select exactly one node: " + key);
 587  
         }
 588  503
         return supportUpdates ? createSubnodeConfiguration(
 589  
                 nodes.get(0), key)
 590  
                 : createSubnodeConfiguration(nodes.get(0));
 591  
     }
 592  
 
 593  
     /**
 594  
      * Returns a hierarchical subnode configuration for the node specified by
 595  
      * the given key. This is a short form for {@code configurationAt(key,
 596  
      * <b>false</b>)}.
 597  
      *
 598  
      * @param key the key that selects the sub tree
 599  
      * @return a hierarchical configuration that contains this sub tree
 600  
      * @see SubnodeConfiguration
 601  
      * @since 1.3
 602  
      */
 603  
     public SubnodeConfiguration configurationAt(String key)
 604  
     {
 605  538
         return configurationAt(key, false);
 606  
     }
 607  
 
 608  
     /**
 609  
      * Returns a list of sub configurations for all configuration nodes selected
 610  
      * by the given key. This method will evaluate the passed in key (using the
 611  
      * current {@code ExpressionEngine}) and then create a subnode
 612  
      * configuration for each returned node (like
 613  
      * {@link #configurationAt(String)}}). This is especially
 614  
      * useful when dealing with list-like structures. As an example consider the
 615  
      * configuration that contains data about database tables and their fields.
 616  
      * If you need access to all fields of a certain table, you can simply do
 617  
      *
 618  
      * <pre>
 619  
      * List fields = config.configurationsAt("tables.table(0).fields.field");
 620  
      * for(Iterator it = fields.iterator(); it.hasNext();)
 621  
      * {
 622  
      *     HierarchicalConfiguration sub = (HierarchicalConfiguration) it.next();
 623  
      *     // now the children and attributes of the field node can be
 624  
      *     // directly accessed
 625  
      *     String fieldName = sub.getString("name");
 626  
      *     String fieldType = sub.getString("type");
 627  
      *     ...
 628  
      * </pre>
 629  
      *
 630  
      * @param key the key for selecting the desired nodes
 631  
      * @return a list with hierarchical configuration objects; each
 632  
      * configuration represents one of the nodes selected by the passed in key
 633  
      * @since 1.3
 634  
      */
 635  
     public List<HierarchicalConfiguration> configurationsAt(String key)
 636  
     {
 637  414
         List<ConfigurationNode> nodes = fetchNodeList(key);
 638  414
         List<HierarchicalConfiguration> configs = new ArrayList<HierarchicalConfiguration>(nodes.size());
 639  414
         for (ConfigurationNode node : nodes)
 640  
         {
 641  285
             configs.add(createSubnodeConfiguration(node));
 642  285
         }
 643  414
         return configs;
 644  
     }
 645  
 
 646  
     /**
 647  
      * Creates a subnode configuration for the specified node. This method is
 648  
      * called by {@code configurationAt()} and
 649  
      * {@code configurationsAt()}.
 650  
      *
 651  
      * @param node the node, for which a subnode configuration is to be created
 652  
      * @return the configuration for the given node
 653  
      * @since 1.3
 654  
      */
 655  
     protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node)
 656  
     {
 657  3128
         SubnodeConfiguration result = new SubnodeConfiguration(this, node);
 658  3128
         registerSubnodeConfiguration(result);
 659  3128
         return result;
 660  
     }
 661  
 
 662  
     /**
 663  
      * Creates a new subnode configuration for the specified node and sets its
 664  
      * construction key. A subnode configuration created this way will be aware
 665  
      * of structural changes of its parent.
 666  
      *
 667  
      * @param node the node, for which a subnode configuration is to be created
 668  
      * @param subnodeKey the key used to construct the configuration
 669  
      * @return the configuration for the given node
 670  
      * @since 1.5
 671  
      */
 672  
     protected SubnodeConfiguration createSubnodeConfiguration(
 673  
             ConfigurationNode node, String subnodeKey)
 674  
     {
 675  9
         SubnodeConfiguration result = createSubnodeConfiguration(node);
 676  9
         result.setSubnodeKey(subnodeKey);
 677  9
         return result;
 678  
     }
 679  
 
 680  
     /**
 681  
      * This method is always called when a subnode configuration created from
 682  
      * this configuration has been modified. This implementation transforms the
 683  
      * received event into an event of type {@code EVENT_SUBNODE_CHANGED}
 684  
      * and notifies the registered listeners.
 685  
      *
 686  
      * @param event the event describing the change
 687  
      * @since 1.5
 688  
      */
 689  
     protected void subnodeConfigurationChanged(ConfigurationEvent event)
 690  
     {
 691  34
         fireEvent(EVENT_SUBNODE_CHANGED, null, event, event.isBeforeUpdate());
 692  34
     }
 693  
 
 694  
     /**
 695  
      * Registers this instance at the given subnode configuration. This
 696  
      * implementation will register a change listener, so that modifications of
 697  
      * the subnode configuration can be tracked.
 698  
      *
 699  
      * @param config the subnode configuration
 700  
      * @since 1.5
 701  
      */
 702  
     void registerSubnodeConfiguration(SubnodeConfiguration config)
 703  
     {
 704  3688
         config.addConfigurationListener(new ConfigurationListener()
 705  3688
         {
 706  
             public void configurationChanged(ConfigurationEvent event)
 707  
             {
 708  34
                 subnodeConfigurationChanged(event);
 709  34
             }
 710  
         });
 711  3688
     }
 712  
 
 713  
     /**
 714  
      * Checks if the specified key is contained in this configuration. Note that
 715  
      * for this configuration the term &quot;contained&quot; means that the key
 716  
      * has an associated value. If there is a node for this key that has no
 717  
      * value but children (either defined or undefined), this method will still
 718  
      * return <b>false </b>.
 719  
      *
 720  
      * @param key the key to be chekced
 721  
      * @return a flag if this key is contained in this configuration
 722  
      */
 723  
     public boolean containsKey(String key)
 724  
     {
 725  1255
         return getProperty(key) != null;
 726  
     }
 727  
 
 728  
     /**
 729  
      * Sets the value of the specified property.
 730  
      *
 731  
      * @param key the key of the property to set
 732  
      * @param value the new value of this property
 733  
      */
 734  
     @Override
 735  
     public void setProperty(String key, Object value)
 736  
     {
 737  815
         fireEvent(EVENT_SET_PROPERTY, key, value, true);
 738  
 
 739  
         // Update the existing nodes for this property
 740  815
         Iterator<ConfigurationNode> itNodes = fetchNodeList(key).iterator();
 741  
         Iterator<?> itValues;
 742  815
         if (!isDelimiterParsingDisabled() || !(value instanceof String))
 743  
         {
 744  804
             itValues = PropertyConverter.toIterator(value, getListDelimiter());
 745  
         }
 746  
         else
 747  
         {
 748  11
             itValues = Collections.singleton(value).iterator();
 749  
         }
 750  
 
 751  926
         while (itNodes.hasNext() && itValues.hasNext())
 752  
         {
 753  111
             itNodes.next().setValue(itValues.next());
 754  
         }
 755  
 
 756  
         // Add additional nodes if necessary
 757  1553
         while (itValues.hasNext())
 758  
         {
 759  738
             addPropertyDirect(key, itValues.next());
 760  
         }
 761  
 
 762  
         // Remove remaining nodes
 763  820
         while (itNodes.hasNext())
 764  
         {
 765  5
             clearNode(itNodes.next());
 766  
         }
 767  
 
 768  815
         fireEvent(EVENT_SET_PROPERTY, key, value, false);
 769  815
     }
 770  
 
 771  
     /**
 772  
      * Clears this configuration. This is a more efficient implementation than
 773  
      * the one inherited from the base class. It directly removes all data from
 774  
      * the root node.
 775  
      */
 776  
     @Override
 777  
     public void clear()
 778  
     {
 779  12476
         fireEvent(EVENT_CLEAR, null, null, true);
 780  12561
         getRootNode().removeAttributes();
 781  12501
         getRootNode().removeChildren();
 782  12558
         getRootNode().setValue(null);
 783  12468
         fireEvent(EVENT_CLEAR, null, null, false);
 784  12562
     }
 785  
 
 786  
     /**
 787  
      * Removes all values of the property with the given name and of keys that
 788  
      * start with this name. So if there is a property with the key
 789  
      * &quot;foo&quot; and a property with the key &quot;foo.bar&quot;, a call
 790  
      * of {@code clearTree("foo")} would remove both properties.
 791  
      *
 792  
      * @param key the key of the property to be removed
 793  
      */
 794  
     public void clearTree(String key)
 795  
     {
 796  9
         fireEvent(EVENT_CLEAR_TREE, key, null, true);
 797  9
         List<ConfigurationNode> nodes = fetchNodeList(key);
 798  
 
 799  9
         for (ConfigurationNode node : nodes)
 800  
         {
 801  9
             removeNode(node);
 802  9
         }
 803  9
         fireEvent(EVENT_CLEAR_TREE, key, nodes, false);
 804  9
     }
 805  
 
 806  
     /**
 807  
      * Removes the property with the given key. Properties with names that start
 808  
      * with the given key (i.e. properties below the specified key in the
 809  
      * hierarchy) won't be affected.
 810  
      *
 811  
      * @param key the key of the property to be removed
 812  
      */
 813  
     @Override
 814  
     public void clearProperty(String key)
 815  
     {
 816  39
         fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
 817  39
         List<ConfigurationNode> nodes = fetchNodeList(key);
 818  
 
 819  39
         for (ConfigurationNode node : nodes)
 820  
         {
 821  127
             clearNode(node);
 822  127
         }
 823  
 
 824  39
         fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
 825  39
     }
 826  
 
 827  
     /**
 828  
      * Returns an iterator with all keys defined in this configuration.
 829  
      * Note that the keys returned by this method will not contain any
 830  
      * indices. This means that some structure will be lost.</p>
 831  
      *
 832  
      * @return an iterator with the defined keys in this configuration
 833  
      */
 834  
     public Iterator<String> getKeys()
 835  
     {
 836  78
         DefinedKeysVisitor visitor = new DefinedKeysVisitor();
 837  78
         getRootNode().visit(visitor);
 838  
 
 839  78
         return visitor.getKeyList().iterator();
 840  
     }
 841  
 
 842  
     /**
 843  
      * Returns an iterator with all keys defined in this configuration that
 844  
      * start with the given prefix. The returned keys will not contain any
 845  
      * indices. This implementation tries to locate a node whose key is the same
 846  
      * as the passed in prefix. Then the subtree of this node is traversed, and
 847  
      * the keys of all nodes encountered (including attributes) are added to the
 848  
      * result set.
 849  
      *
 850  
      * @param prefix the prefix of the keys to start with
 851  
      * @return an iterator with the found keys
 852  
      */
 853  
     @Override
 854  
     public Iterator<String> getKeys(String prefix)
 855  
     {
 856  14
         DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
 857  14
         if (containsKey(prefix))
 858  
         {
 859  
             // explicitly add the prefix
 860  2
             visitor.getKeyList().add(prefix);
 861  
         }
 862  
 
 863  14
         List<ConfigurationNode> nodes = fetchNodeList(prefix);
 864  
 
 865  14
         for (ConfigurationNode node : nodes)
 866  
         {
 867  14
             for (ConfigurationNode c : node.getChildren())
 868  
             {
 869  29
                 c.visit(visitor);
 870  29
             }
 871  14
             for (ConfigurationNode attr : node.getAttributes())
 872  
             {
 873  2
                 attr.visit(visitor);
 874  2
             }
 875  14
         }
 876  
 
 877  14
         return visitor.getKeyList().iterator();
 878  
     }
 879  
 
 880  
     /**
 881  
      * Returns the maximum defined index for the given key. This is useful if
 882  
      * there are multiple values for this key. They can then be addressed
 883  
      * separately by specifying indices from 0 to the return value of this
 884  
      * method.
 885  
      *
 886  
      * @param key the key to be checked
 887  
      * @return the maximum defined index for this key
 888  
      */
 889  
     public int getMaxIndex(String key)
 890  
     {
 891  367
         return fetchNodeList(key).size() - 1;
 892  
     }
 893  
 
 894  
     /**
 895  
      * Creates a copy of this object. This new configuration object will contain
 896  
      * copies of all nodes in the same structure. Registered event listeners
 897  
      * won't be cloned; so they are not registered at the returned copy.
 898  
      *
 899  
      * @return the copy
 900  
      * @since 1.2
 901  
      */
 902  
     @Override
 903  
     public Object clone()
 904  
     {
 905  
         try
 906  
         {
 907  11
             HierarchicalConfiguration copy = (HierarchicalConfiguration) super
 908  
                     .clone();
 909  
 
 910  
             // clone the nodes, too
 911  11
             CloneVisitor v = new CloneVisitor();
 912  11
             getRootNode().visit(v);
 913  11
             copy.setRootNode(v.getClone());
 914  
 
 915  11
             return copy;
 916  
         }
 917  0
         catch (CloneNotSupportedException cex)
 918  
         {
 919  
             // should not happen
 920  0
             throw new ConfigurationRuntimeException(cex);
 921  
         }
 922  
     }
 923  
 
 924  
     /**
 925  
      * Returns a configuration with the same content as this configuration, but
 926  
      * with all variables replaced by their actual values. This implementation
 927  
      * is specific for hierarchical configurations. It clones the current
 928  
      * configuration and runs a specialized visitor on the clone, which performs
 929  
      * interpolation on the single configuration nodes.
 930  
      *
 931  
      * @return a configuration with all variables interpolated
 932  
      * @since 1.5
 933  
      */
 934  
     @Override
 935  
     public Configuration interpolatedConfiguration()
 936  
     {
 937  1
         HierarchicalConfiguration c = (HierarchicalConfiguration) clone();
 938  1
         c.getRootNode().visit(new ConfigurationNodeVisitorAdapter()
 939  1
         {
 940  
             @Override
 941  
             public void visitAfterChildren(ConfigurationNode node)
 942  
             {
 943  39
                 node.setValue(interpolate(node.getValue()));
 944  39
             }
 945  
         });
 946  1
         return c;
 947  
     }
 948  
 
 949  
     /**
 950  
      * Helper method for fetching a list of all nodes that are addressed by the
 951  
      * specified key.
 952  
      *
 953  
      * @param key the key
 954  
      * @return a list with all affected nodes (never <b>null </b>)
 955  
      */
 956  
     protected List<ConfigurationNode> fetchNodeList(String key)
 957  
     {
 958  641575
         return getExpressionEngine().query(getRootNode(), key);
 959  
     }
 960  
 
 961  
     /**
 962  
      * Recursive helper method for fetching a property. This method processes
 963  
      * all facets of a configuration key, traverses the tree of properties and
 964  
      * fetches the the nodes of all matching properties.
 965  
      *
 966  
      * @param keyPart the configuration key iterator
 967  
      * @param node the actual node
 968  
      * @param nodes here the found nodes are stored
 969  
      * @deprecated Property keys are now evaluated by the expression engine
 970  
      * associated with the configuration; this method will no longer be called.
 971  
      * If you want to modify the way properties are looked up, consider
 972  
      * implementing you own {@code ExpressionEngine} implementation.
 973  
      */
 974  
     @Deprecated
 975  
     protected void findPropertyNodes(ConfigurationKey.KeyIterator keyPart,
 976  
             Node node, Collection<ConfigurationNode> nodes)
 977  
     {
 978  0
     }
 979  
 
 980  
     /**
 981  
      * Checks if the specified node is defined.
 982  
      *
 983  
      * @param node the node to be checked
 984  
      * @return a flag if this node is defined
 985  
      * @deprecated Use the method {@link #nodeDefined(ConfigurationNode)}
 986  
      * instead.
 987  
      */
 988  
     @Deprecated
 989  
     protected boolean nodeDefined(Node node)
 990  
     {
 991  0
         return nodeDefined((ConfigurationNode) node);
 992  
     }
 993  
 
 994  
     /**
 995  
      * Checks if the specified node is defined.
 996  
      *
 997  
      * @param node the node to be checked
 998  
      * @return a flag if this node is defined
 999  
      */
 1000  
     protected boolean nodeDefined(ConfigurationNode node)
 1001  
     {
 1002  310
         DefinedVisitor visitor = new DefinedVisitor();
 1003  310
         node.visit(visitor);
 1004  310
         return visitor.isDefined();
 1005  
     }
 1006  
 
 1007  
     /**
 1008  
      * Removes the specified node from this configuration. This method ensures
 1009  
      * that parent nodes that become undefined by this operation are also
 1010  
      * removed.
 1011  
      *
 1012  
      * @param node the node to be removed
 1013  
      * @deprecated Use the method {@link #removeNode(ConfigurationNode)}
 1014  
      * instead.
 1015  
      */
 1016  
     @Deprecated
 1017  
     protected void removeNode(Node node)
 1018  
     {
 1019  0
         removeNode((ConfigurationNode) node);
 1020  0
     }
 1021  
 
 1022  
     /**
 1023  
      * Removes the specified node from this configuration. This method ensures
 1024  
      * that parent nodes that become undefined by this operation are also
 1025  
      * removed.
 1026  
      *
 1027  
      * @param node the node to be removed
 1028  
      */
 1029  
     protected void removeNode(ConfigurationNode node)
 1030  
     {
 1031  120
         ConfigurationNode parent = node.getParentNode();
 1032  120
         if (parent != null)
 1033  
         {
 1034  111
             parent.removeChild(node);
 1035  111
             if (!nodeDefined(parent))
 1036  
             {
 1037  24
                 removeNode(parent);
 1038  
             }
 1039  
         }
 1040  120
     }
 1041  
 
 1042  
     /**
 1043  
      * Clears the value of the specified node. If the node becomes undefined by
 1044  
      * this operation, it is removed from the hierarchy.
 1045  
      *
 1046  
      * @param node the node to be cleared
 1047  
      * @deprecated Use the method {@link #clearNode(ConfigurationNode)}
 1048  
      * instead
 1049  
      */
 1050  
     @Deprecated
 1051  
     protected void clearNode(Node node)
 1052  
     {
 1053  0
         clearNode((ConfigurationNode) node);
 1054  0
     }
 1055  
 
 1056  
     /**
 1057  
      * Clears the value of the specified node. If the node becomes undefined by
 1058  
      * this operation, it is removed from the hierarchy.
 1059  
      *
 1060  
      * @param node the node to be cleared
 1061  
      */
 1062  
     protected void clearNode(ConfigurationNode node)
 1063  
     {
 1064  132
         node.setValue(null);
 1065  132
         if (!nodeDefined(node))
 1066  
         {
 1067  87
             removeNode(node);
 1068  
         }
 1069  132
     }
 1070  
 
 1071  
     /**
 1072  
      * Returns a reference to the parent node of an add operation. Nodes for new
 1073  
      * properties can be added as children of this node. If the path for the
 1074  
      * specified key does not exist so far, it is created now.
 1075  
      *
 1076  
      * @param keyIt the iterator for the key of the new property
 1077  
      * @param startNode the node to start the search with
 1078  
      * @return the parent node for the add operation
 1079  
      * @deprecated Adding new properties is now to a major part delegated to the
 1080  
      * {@code ExpressionEngine} associated with this configuration instance.
 1081  
      * This method will no longer be called. Developers who want to modify the
 1082  
      * process of adding new properties should consider implementing their own
 1083  
      * expression engine.
 1084  
      */
 1085  
     @Deprecated
 1086  
     protected Node fetchAddNode(ConfigurationKey.KeyIterator keyIt, Node startNode)
 1087  
     {
 1088  0
         return null;
 1089  
     }
 1090  
 
 1091  
     /**
 1092  
      * Finds the last existing node for an add operation. This method traverses
 1093  
      * the configuration tree along the specified key. The last existing node on
 1094  
      * this path is returned.
 1095  
      *
 1096  
      * @param keyIt the key iterator
 1097  
      * @param node the actual node
 1098  
      * @return the last existing node on the given path
 1099  
      * @deprecated Adding new properties is now to a major part delegated to the
 1100  
      * {@code ExpressionEngine} associated with this configuration instance.
 1101  
      * This method will no longer be called. Developers who want to modify the
 1102  
      * process of adding new properties should consider implementing their own
 1103  
      * expression engine.
 1104  
      */
 1105  
     @Deprecated
 1106  
     protected Node findLastPathNode(ConfigurationKey.KeyIterator keyIt, Node node)
 1107  
     {
 1108  0
         return null;
 1109  
     }
 1110  
 
 1111  
     /**
 1112  
      * Creates the missing nodes for adding a new property. This method ensures
 1113  
      * that there are corresponding nodes for all components of the specified
 1114  
      * configuration key.
 1115  
      *
 1116  
      * @param keyIt the key iterator
 1117  
      * @param root the base node of the path to be created
 1118  
      * @return the last node of the path
 1119  
      * @deprecated Adding new properties is now to a major part delegated to the
 1120  
      * {@code ExpressionEngine} associated with this configuration instance.
 1121  
      * This method will no longer be called. Developers who want to modify the
 1122  
      * process of adding new properties should consider implementing their own
 1123  
      * expression engine.
 1124  
      */
 1125  
     @Deprecated
 1126  
     protected Node createAddPath(ConfigurationKey.KeyIterator keyIt, Node root)
 1127  
     {
 1128  0
         return null;
 1129  
     }
 1130  
 
 1131  
     /**
 1132  
      * Creates a new {@code Node} object with the specified name. This
 1133  
      * method can be overloaded in derived classes if a specific node type is
 1134  
      * needed. This base implementation always returns a new object of the
 1135  
      * {@code Node} class.
 1136  
      *
 1137  
      * @param name the name of the new node
 1138  
      * @return the new node
 1139  
      */
 1140  
     protected Node createNode(String name)
 1141  
     {
 1142  10012
         return new Node(name);
 1143  
     }
 1144  
 
 1145  
     /**
 1146  
      * Helper method for processing a node add data object obtained from the
 1147  
      * expression engine. This method will create all new nodes.
 1148  
      *
 1149  
      * @param data the data object
 1150  
      * @return the new node
 1151  
      * @since 1.3
 1152  
      */
 1153  
     private ConfigurationNode processNodeAddData(NodeAddData data)
 1154  
     {
 1155  10163
         ConfigurationNode node = data.getParent();
 1156  
 
 1157  
         // Create missing nodes on the path
 1158  10163
         for (String name : data.getPathNodes())
 1159  
         {
 1160  3126
             ConfigurationNode child = createNode(name);
 1161  3126
             node.addChild(child);
 1162  3126
             node = child;
 1163  3126
         }
 1164  
 
 1165  
         // Add new target node
 1166  10163
         ConfigurationNode child = createNode(data.getNewNodeName());
 1167  10163
         if (data.isAttribute())
 1168  
         {
 1169  170
             node.addAttribute(child);
 1170  
         }
 1171  
         else
 1172  
         {
 1173  9993
             node.addChild(child);
 1174  
         }
 1175  10163
         return child;
 1176  
     }
 1177  
 
 1178  
     /**
 1179  
      * Clears all reference fields in a node structure. A configuration node can
 1180  
      * store a so-called &quot;reference&quot;. The meaning of this data is
 1181  
      * determined by a concrete sub class. Typically such references are
 1182  
      * specific for a configuration instance. If this instance is cloned or
 1183  
      * copied, they must be cleared. This can be done using this method.
 1184  
      *
 1185  
      * @param node the root node of the node hierarchy, in which the references
 1186  
      * are to be cleared
 1187  
      * @since 1.4
 1188  
      */
 1189  
     protected static void clearReferences(ConfigurationNode node)
 1190  
     {
 1191  76
         node.visit(new ConfigurationNodeVisitorAdapter()
 1192  76
         {
 1193  
             @Override
 1194  
             public void visitBeforeChildren(ConfigurationNode node)
 1195  
             {
 1196  1045
                 node.setReference(null);
 1197  1045
             }
 1198  
         });
 1199  76
     }
 1200  
 
 1201  
     /**
 1202  
      * Transforms the specified object into a Node. This method treats view
 1203  
      * nodes in a special way. This is necessary because ViewNode does not
 1204  
      * extend HierarchicalConfiguration.Node; thus the API for the node visitor
 1205  
      * is slightly different. Therefore a view node is transformed into a
 1206  
      * special compatibility Node object.
 1207  
      *
 1208  
      * @param obj the original node object
 1209  
      * @return the node to be used
 1210  
      */
 1211  
     private static Node getNodeFor(Object obj)
 1212  
     {
 1213  
         Node nd;
 1214  9167
         if (obj instanceof ViewNode)
 1215  
         {
 1216  2
             final ViewNode viewNode = (ViewNode) obj;
 1217  2
             nd = new Node(viewNode)
 1218  2
             {
 1219  
                 @Override
 1220  
                 public void setReference(Object reference)
 1221  
                 {
 1222  3
                     super.setReference(reference);
 1223  
                     // also set the reference at the original node
 1224  3
                     viewNode.setReference(reference);
 1225  3
                 }
 1226  
             };
 1227  2
         }
 1228  
         else
 1229  
         {
 1230  9165
             nd = (Node) obj;
 1231  
         }
 1232  9167
         return nd;
 1233  
     }
 1234  
 
 1235  
     /**
 1236  
      * A data class for storing (hierarchical) property information. A property
 1237  
      * can have a value and an arbitrary number of child properties. From
 1238  
      * version 1.3 on this class is only a thin wrapper over the
 1239  
      * {@link org.apache.commons.configuration.tree.DefaultConfigurationNode DefaultconfigurationNode}
 1240  
      * class that exists mainly for the purpose of backwards compatibility.
 1241  
      */
 1242  
     public static class Node extends DefaultConfigurationNode implements Serializable
 1243  
     {
 1244  
         /**
 1245  
          * The serial version UID.
 1246  
          */
 1247  
         private static final long serialVersionUID = -6357500633536941775L;
 1248  
 
 1249  
         /**
 1250  
          * Creates a new instance of {@code Node}.
 1251  
          */
 1252  
         public Node()
 1253  
         {
 1254  26178
             super();
 1255  26203
         }
 1256  
 
 1257  
         /**
 1258  
          * Creates a new instance of {@code Node} and sets the name.
 1259  
          *
 1260  
          * @param name the node's name
 1261  
          */
 1262  
         public Node(String name)
 1263  
         {
 1264  179591
             super(name);
 1265  179621
         }
 1266  
 
 1267  
         /**
 1268  
          * Creates a new instance of {@code Node} and sets the name and the value.
 1269  
          *
 1270  
          * @param name the node's name
 1271  
          * @param value the value
 1272  
          */
 1273  
         public Node(String name, Object value)
 1274  
         {
 1275  12
             super(name, value);
 1276  12
         }
 1277  
 
 1278  
         /**
 1279  
          * Creates a new instance of {@code Node} based on the given
 1280  
          * source node. All properties of the source node, including its
 1281  
          * children and attributes, will be copied.
 1282  
          *
 1283  
          * @param src the node to be copied
 1284  
          */
 1285  
         public Node(ConfigurationNode src)
 1286  
         {
 1287  9
             this(src.getName(), src.getValue());
 1288  9
             setReference(src.getReference());
 1289  9
             for (ConfigurationNode nd : src.getChildren())
 1290  
             {
 1291  
                 // Don't change the parent node
 1292  19
                 ConfigurationNode parent = nd.getParentNode();
 1293  19
                 addChild(nd);
 1294  19
                 nd.setParentNode(parent);
 1295  19
             }
 1296  
 
 1297  9
             for (ConfigurationNode nd : src.getAttributes())
 1298  
             {
 1299  
                 // Don't change the parent node
 1300  2
                 ConfigurationNode parent = nd.getParentNode();
 1301  2
                 addAttribute(nd);
 1302  2
                 nd.setParentNode(parent);
 1303  2
             }
 1304  9
         }
 1305  
 
 1306  
         /**
 1307  
          * Returns the parent of this node.
 1308  
          *
 1309  
          * @return this node's parent (can be <b>null</b>)
 1310  
          */
 1311  
         public Node getParent()
 1312  
         {
 1313  2808
             return (Node) getParentNode();
 1314  
         }
 1315  
 
 1316  
         /**
 1317  
          * Sets the parent of this node.
 1318  
          *
 1319  
          * @param node the parent node
 1320  
          */
 1321  
         public void setParent(Node node)
 1322  
         {
 1323  0
             setParentNode(node);
 1324  0
         }
 1325  
 
 1326  
         /**
 1327  
          * Adds the given node to the children of this node.
 1328  
          *
 1329  
          * @param node the child to be added
 1330  
          */
 1331  
         public void addChild(Node node)
 1332  
         {
 1333  113808
             addChild((ConfigurationNode) node);
 1334  113812
         }
 1335  
 
 1336  
         /**
 1337  
          * Returns a flag whether this node has child elements.
 1338  
          *
 1339  
          * @return <b>true</b> if there is a child node, <b>false</b> otherwise
 1340  
          */
 1341  
         public boolean hasChildren()
 1342  
         {
 1343  54157
             return getChildrenCount() > 0 || getAttributeCount() > 0;
 1344  
         }
 1345  
 
 1346  
         /**
 1347  
          * Removes the specified child from this node.
 1348  
          *
 1349  
          * @param child the child node to be removed
 1350  
          * @return a flag if the child could be found
 1351  
          */
 1352  
         public boolean remove(Node child)
 1353  
         {
 1354  3079
             return child.isAttribute() ? removeAttribute(child) : removeChild(child);
 1355  
         }
 1356  
 
 1357  
         /**
 1358  
          * Removes all children with the given name.
 1359  
          *
 1360  
          * @param name the name of the children to be removed
 1361  
          * @return a flag if children with this name existed
 1362  
          */
 1363  
         public boolean remove(String name)
 1364  
         {
 1365  5
             boolean childrenRemoved = removeChild(name);
 1366  5
             boolean attrsRemoved = removeAttribute(name);
 1367  5
             return childrenRemoved || attrsRemoved;
 1368  
         }
 1369  
 
 1370  
         /**
 1371  
          * A generic method for traversing this node and all of its children.
 1372  
          * This method sends the passed in visitor to this node and all of its
 1373  
          * children.
 1374  
          *
 1375  
          * @param visitor the visitor
 1376  
          * @param key here a configuration key with the name of the root node of
 1377  
          * the iteration can be passed; if this key is not <b>null </b>, the
 1378  
          * full paths to the visited nodes are builded and passed to the
 1379  
          * visitor's {@code visit()} methods
 1380  
          */
 1381  
         public void visit(NodeVisitor visitor, ConfigurationKey key)
 1382  
         {
 1383  4718
             int length = 0;
 1384  4718
             if (key != null)
 1385  
             {
 1386  28
                 length = key.length();
 1387  28
                 if (getName() != null)
 1388  
                 {
 1389  27
                     key
 1390  
                             .append(StringUtils
 1391  
                                     .replace(
 1392  
                                             isAttribute() ? ConfigurationKey
 1393  
                                                     .constructAttributeKey(getName())
 1394  
                                                     : getName(),
 1395  
                                             String
 1396  
                                                     .valueOf(ConfigurationKey.PROPERTY_DELIMITER),
 1397  
                                             ConfigurationKey.ESCAPED_DELIMITER));
 1398  
                 }
 1399  
             }
 1400  
 
 1401  4718
             visitor.visitBeforeChildren(this, key);
 1402  
 
 1403  4718
             for (Iterator<ConfigurationNode> it = getChildren().iterator(); it.hasNext()
 1404  8063
                     && !visitor.terminate();)
 1405  
             {
 1406  3345
                 Object obj = it.next();
 1407  3345
                 getNodeFor(obj).visit(visitor, key);
 1408  3345
             }
 1409  4718
             for (Iterator<ConfigurationNode> it = getAttributes().iterator(); it.hasNext()
 1410  6003
                     && !visitor.terminate();)
 1411  
             {
 1412  1285
                 Object obj = it.next();
 1413  1285
                 getNodeFor(obj).visit(visitor, key);
 1414  1285
             }
 1415  
 
 1416  4718
             visitor.visitAfterChildren(this, key);
 1417  4718
             if (key != null)
 1418  
             {
 1419  28
                 key.setLength(length);
 1420  
             }
 1421  4718
         }
 1422  
     }
 1423  
 
 1424  
     /**
 1425  
      * <p>Definition of a visitor class for traversing a node and all of its
 1426  
      * children.</p><p>This class defines the interface of a visitor for
 1427  
      * {@code Node} objects and provides a default implementation. The
 1428  
      * method {@code visit()} of {@code Node} implements a generic
 1429  
      * iteration algorithm based on the <em>Visitor</em> pattern. By providing
 1430  
      * different implementations of visitors it is possible to collect different
 1431  
      * data during the iteration process.</p>
 1432  
      *
 1433  
      */
 1434  88
     public static class NodeVisitor
 1435  
     {
 1436  
         /**
 1437  
          * Visits the specified node. This method is called during iteration for
 1438  
          * each node before its children have been visited.
 1439  
          *
 1440  
          * @param node the actual node
 1441  
          * @param key the key of this node (may be <b>null </b>)
 1442  
          */
 1443  
         public void visitBeforeChildren(Node node, ConfigurationKey key)
 1444  
         {
 1445  56
         }
 1446  
 
 1447  
         /**
 1448  
          * Visits the specified node after its children have been processed.
 1449  
          * This gives a visitor the opportunity of collecting additional data
 1450  
          * after the child nodes have been visited.
 1451  
          *
 1452  
          * @param node the node to be visited
 1453  
          * @param key the key of this node (may be <b>null </b>)
 1454  
          */
 1455  
         public void visitAfterChildren(Node node, ConfigurationKey key)
 1456  
         {
 1457  4678
         }
 1458  
 
 1459  
         /**
 1460  
          * Returns a flag that indicates if iteration should be stopped. This
 1461  
          * method is called after each visited node. It can be useful for
 1462  
          * visitors that search a specific node. If this node is found, the
 1463  
          * whole process can be stopped. This base implementation always returns
 1464  
          * <b>false </b>.
 1465  
          *
 1466  
          * @return a flag if iteration should be stopped
 1467  
          */
 1468  
         public boolean terminate()
 1469  
         {
 1470  4591
             return false;
 1471  
         }
 1472  
     }
 1473  
 
 1474  
     /**
 1475  
      * A specialized visitor that checks if a node is defined.
 1476  
      * &quot;Defined&quot; in this terms means that the node or at least one of
 1477  
      * its sub nodes is associated with a value.
 1478  
      *
 1479  
      */
 1480  310
     static class DefinedVisitor extends ConfigurationNodeVisitorAdapter
 1481  
     {
 1482  
         /** Stores the defined flag. */
 1483  
         private boolean defined;
 1484  
 
 1485  
         /**
 1486  
          * Checks if iteration should be stopped. This can be done if the first
 1487  
          * defined node is found.
 1488  
          *
 1489  
          * @return a flag if iteration should be stopped
 1490  
          */
 1491  
         @Override
 1492  
         public boolean terminate()
 1493  
         {
 1494  901
             return isDefined();
 1495  
         }
 1496  
 
 1497  
         /**
 1498  
          * Visits the node. Checks if a value is defined.
 1499  
          *
 1500  
          * @param node the actual node
 1501  
          */
 1502  
         @Override
 1503  
         public void visitBeforeChildren(ConfigurationNode node)
 1504  
         {
 1505  533
             defined = node.getValue() != null;
 1506  533
         }
 1507  
 
 1508  
         /**
 1509  
          * Returns the defined flag.
 1510  
          *
 1511  
          * @return the defined flag
 1512  
          */
 1513  
         public boolean isDefined()
 1514  
         {
 1515  1211
             return defined;
 1516  
         }
 1517  
     }
 1518  
 
 1519  
     /**
 1520  
      * A specialized visitor that fills a list with keys that are defined in a
 1521  
      * node hierarchy.
 1522  
      */
 1523  
     class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter
 1524  
     {
 1525  
         /** Stores the list to be filled. */
 1526  
         private Set<String> keyList;
 1527  
 
 1528  
         /** A stack with the keys of the already processed nodes. */
 1529  
         private Stack<String> parentKeys;
 1530  
 
 1531  
         /**
 1532  
          * Default constructor.
 1533  
          */
 1534  
         public DefinedKeysVisitor()
 1535  92
         {
 1536  92
             keyList = new LinkedHashSet<String>();
 1537  92
             parentKeys = new Stack<String>();
 1538  92
         }
 1539  
 
 1540  
         /**
 1541  
          * Creates a new {@code DefinedKeysVisitor} instance and sets the
 1542  
          * prefix for the keys to fetch.
 1543  
          *
 1544  
          * @param prefix the prefix
 1545  
          */
 1546  
         public DefinedKeysVisitor(String prefix)
 1547  
         {
 1548  14
             this();
 1549  14
             parentKeys.push(prefix);
 1550  14
         }
 1551  
 
 1552  
         /**
 1553  
          * Returns the list with all defined keys.
 1554  
          *
 1555  
          * @return the list with the defined keys
 1556  
          */
 1557  
         public Set<String> getKeyList()
 1558  
         {
 1559  94
             return keyList;
 1560  
         }
 1561  
 
 1562  
         /**
 1563  
          * Visits the node after its children has been processed. Removes this
 1564  
          * node's key from the stack.
 1565  
          *
 1566  
          * @param node the node
 1567  
          */
 1568  
         @Override
 1569  
         public void visitAfterChildren(ConfigurationNode node)
 1570  
         {
 1571  3118
             parentKeys.pop();
 1572  3118
         }
 1573  
 
 1574  
         /**
 1575  
          * Visits the specified node. If this node has a value, its key is added
 1576  
          * to the internal list.
 1577  
          *
 1578  
          * @param node the node to be visited
 1579  
          */
 1580  
         @Override
 1581  
         public void visitBeforeChildren(ConfigurationNode node)
 1582  
         {
 1583  3118
             String parentKey = parentKeys.isEmpty() ? null
 1584  
                     : (String) parentKeys.peek();
 1585  3118
             String key = getExpressionEngine().nodeKey(node, parentKey);
 1586  3118
             parentKeys.push(key);
 1587  3118
             if (node.getValue() != null)
 1588  
             {
 1589  2408
                 keyList.add(key);
 1590  
             }
 1591  3118
         }
 1592  
     }
 1593  
 
 1594  
     /**
 1595  
      * A specialized visitor that is able to create a deep copy of a node
 1596  
      * hierarchy.
 1597  
      */
 1598  
     static class CloneVisitor extends ConfigurationNodeVisitorAdapter
 1599  
     {
 1600  
         /** A stack with the actual object to be copied. */
 1601  
         private Stack<ConfigurationNode> copyStack;
 1602  
 
 1603  
         /** Stores the result of the clone process. */
 1604  
         private ConfigurationNode result;
 1605  
 
 1606  
         /**
 1607  
          * Creates a new instance of {@code CloneVisitor}.
 1608  
          */
 1609  
         public CloneVisitor()
 1610  7091
         {
 1611  7090
             copyStack = new Stack<ConfigurationNode>();
 1612  7091
         }
 1613  
 
 1614  
         /**
 1615  
          * Visits the specified node after its children have been processed.
 1616  
          *
 1617  
          * @param node the node
 1618  
          */
 1619  
         @Override
 1620  
         public void visitAfterChildren(ConfigurationNode node)
 1621  
         {
 1622  51234
             ConfigurationNode copy = copyStack.pop();
 1623  51234
             if (copyStack.isEmpty())
 1624  
             {
 1625  7117
                 result = copy;
 1626  
             }
 1627  51239
         }
 1628  
 
 1629  
         /**
 1630  
          * Visits and copies the specified node.
 1631  
          *
 1632  
          * @param node the node
 1633  
          */
 1634  
         @Override
 1635  
         public void visitBeforeChildren(ConfigurationNode node)
 1636  
         {
 1637  51233
             ConfigurationNode copy = (ConfigurationNode) node.clone();
 1638  51236
             copy.setParentNode(null);
 1639  
 
 1640  51236
             if (!copyStack.isEmpty())
 1641  
             {
 1642  44122
                 if (node.isAttribute())
 1643  
                 {
 1644  15006
                     copyStack.peek().addAttribute(copy);
 1645  
                 }
 1646  
                 else
 1647  
                 {
 1648  29117
                     copyStack.peek().addChild(copy);
 1649  
                 }
 1650  
             }
 1651  
 
 1652  51238
             copyStack.push(copy);
 1653  51238
         }
 1654  
 
 1655  
         /**
 1656  
          * Returns the result of the clone process. This is the root node of the
 1657  
          * cloned node hierarchy.
 1658  
          *
 1659  
          * @return the cloned root node
 1660  
          */
 1661  
         public ConfigurationNode getClone()
 1662  
         {
 1663  7170
             return result;
 1664  
         }
 1665  
     }
 1666  
 
 1667  
     /**
 1668  
      * A specialized visitor base class that can be used for storing the tree of
 1669  
      * configuration nodes. The basic idea is that each node can be associated
 1670  
      * with a reference object. This reference object has a concrete meaning in
 1671  
      * a derived class, e.g. an entry in a JNDI context or an XML element. When
 1672  
      * the configuration tree is set up, the {@code load()} method is
 1673  
      * responsible for setting the reference objects. When the configuration
 1674  
      * tree is later modified, new nodes do not have a defined reference object.
 1675  
      * This visitor class processes all nodes and finds the ones without a
 1676  
      * defined reference object. For those nodes the {@code insert()}
 1677  
      * method is called, which must be defined in concrete sub classes. This
 1678  
      * method can perform all steps to integrate the new node into the original
 1679  
      * structure.
 1680  
      *
 1681  
      */
 1682  85
     protected abstract static class BuilderVisitor extends NodeVisitor
 1683  
     {
 1684  
         /**
 1685  
          * Visits the specified node before its children have been traversed.
 1686  
          *
 1687  
          * @param node the node to visit
 1688  
          * @param key the current key
 1689  
          */
 1690  
         @Override
 1691  
         public void visitBeforeChildren(Node node, ConfigurationKey key)
 1692  
         {
 1693  4622
             Collection<ConfigurationNode> subNodes = new LinkedList<ConfigurationNode>(node.getChildren());
 1694  4622
             subNodes.addAll(node.getAttributes());
 1695  4622
             Iterator<ConfigurationNode> children = subNodes.iterator();
 1696  4622
             Node sibling1 = null;
 1697  4622
             Node nd = null;
 1698  
 
 1699  6604
             while (children.hasNext())
 1700  
             {
 1701  
                 // find the next new node
 1702  
                 do
 1703  
                 {
 1704  4059
                     sibling1 = nd;
 1705  4059
                     Object obj = children.next();
 1706  4059
                     nd = getNodeFor(obj);
 1707  4059
                 } while (nd.getReference() != null && children.hasNext());
 1708  
 
 1709  1982
                 if (nd.getReference() == null)
 1710  
                 {
 1711  
                     // find all following new nodes
 1712  436
                     List<Node> newNodes = new LinkedList<Node>();
 1713  436
                     newNodes.add(nd);
 1714  887
                     while (children.hasNext())
 1715  
                     {
 1716  478
                         Object obj = children.next();
 1717  478
                         nd = getNodeFor(obj);
 1718  478
                         if (nd.getReference() == null)
 1719  
                         {
 1720  451
                             newNodes.add(nd);
 1721  
                         }
 1722  
                         else
 1723  
                         {
 1724  
                             break;
 1725  
                         }
 1726  451
                     }
 1727  
 
 1728  
                     // Insert all new nodes
 1729  436
                     Node sibling2 = (nd.getReference() == null) ? null : nd;
 1730  436
                     for (Node insertNode : newNodes)
 1731  
                     {
 1732  887
                         if (insertNode.getReference() == null)
 1733  
                         {
 1734  875
                             Object ref = insert(insertNode, node, sibling1, sibling2);
 1735  875
                             if (ref != null)
 1736  
                             {
 1737  618
                                 insertNode.setReference(ref);
 1738  
                             }
 1739  875
                             sibling1 = insertNode;
 1740  
                         }
 1741  887
                     }
 1742  436
                 }
 1743  
             }
 1744  4622
         }
 1745  
 
 1746  
         /**
 1747  
          * Inserts a new node into the structure constructed by this builder.
 1748  
          * This method is called for each node that has been added to the
 1749  
          * configuration tree after the configuration has been loaded from its
 1750  
          * source. These new nodes have to be inserted into the original
 1751  
          * structure. The passed in nodes define the position of the node to be
 1752  
          * inserted: its parent and the siblings between to insert. The return
 1753  
          * value is interpreted as the new reference of the affected
 1754  
          * {@code Node} object; if it is not <b>null </b>, it is passed
 1755  
          * to the node's {@code setReference()} method.
 1756  
          *
 1757  
          * @param newNode the node to be inserted
 1758  
          * @param parent the parent node
 1759  
          * @param sibling1 the sibling after which the node is to be inserted;
 1760  
          * can be <b>null </b> if the new node is going to be the first child
 1761  
          * node
 1762  
          * @param sibling2 the sibling before which the node is to be inserted;
 1763  
          * can be <b>null </b> if the new node is going to be the last child
 1764  
          * node
 1765  
          * @return the reference object for the node to be inserted
 1766  
          */
 1767  
         protected abstract Object insert(Node newNode, Node parent, Node sibling1, Node sibling2);
 1768  
     }
 1769  
 }