View Javadoc

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