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