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.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     {
    172         setRootNode(new Node());
    173     }
    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         this();
    187         if (c != null)
    188         {
    189             CloneVisitor visitor = new CloneVisitor();
    190             c.getRootNode().visit(visitor);
    191             setRootNode(visitor.getClone());
    192         }
    193     }
    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         if (root == null && rootNode != null)
    206         {
    207             // Dynamically create a snapshot of the root node
    208             return new Node(rootNode);
    209         }
    210
    211         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         if (node == null)
    225         {
    226             throw new IllegalArgumentException("Root node must not be null!");
    227         }
    228         root = node;
    229         rootNode = null;
    230     }
    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         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         if (rootNode == null)
    252         {
    253             throw new IllegalArgumentException("Root node must not be null!");
    254         }
    255         this.rootNode = rootNode;
    256
    257         // For backward compatibility also set the old root field.
    258         root = (rootNode instanceof Node) ? (Node) rootNode : null;
    259     }
    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         if (defaultExpressionEngine == null)
    270         {
    271             defaultExpressionEngine = new DefaultExpressionEngine();
    272         }
    273         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         if (engine == null)
    288         {
    289             throw new IllegalArgumentException(
    290                     "Default expression engine must not be null!");
    291         }
    292         defaultExpressionEngine = engine;
    293     }
    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         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         this.expressionEngine = expressionEngine;
    321     }
    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         List nodes = fetchNodeList(key);
    333
    334         if (nodes.size() == 0)
    335         {
    336             return null;
    337         }
    338         else
    339         {
    340             List list = new ArrayList();
    341             for (Iterator it = nodes.iterator(); it.hasNext();)
    342             {
    343                 ConfigurationNode node = (ConfigurationNode) it.next();
    344                 if (node.getValue() != null)
    345                 {
    346                     list.add(node.getValue());
    347                 }
    348             }
    349
    350             if (list.size() < 1)
    351             {
    352                 return null;
    353             }
    354             else
    355             {
    356                 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         NodeAddData data = getExpressionEngine().prepareAdd(getRootNode(), key);
    372         ConfigurationNode node = processNodeAddData(data);
    373         node.setValue(obj);
    374     }
    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         if (nodes == null || nodes.isEmpty())
    400         {
    401             return;
    402         }
    403
    404         fireEvent(EVENT_ADD_NODES, key, nodes, true);
    405         ConfigurationNode parent;
    406         List target = fetchNodeList(key);
    407         if (target.size() == 1)
    408         {
    409             // existing unique key
    410             parent = (ConfigurationNode) target.get(0);
    411         }
    412         else
    413         {
    414             // otherwise perform an add operation
    415             parent = processNodeAddData(getExpressionEngine().prepareAdd(
    416                     getRootNode(), key));
    417         }
    418
    419         if (parent.isAttribute())
    420         {
    421             throw new IllegalArgumentException(
    422                     "Cannot add nodes to an attribute node!");
    423         }
    424
    425         for (Iterator it = nodes.iterator(); it.hasNext();)
    426         {
    427             ConfigurationNode child = (ConfigurationNode) it.next();
    428             if (child.isAttribute())
    429             {
    430                 parent.addAttribute(child);
    431             }
    432             else
    433             {
    434                 parent.addChild(child);
    435             }
    436             clearReferences(child);
    437         }
    438         fireEvent(EVENT_ADD_NODES, key, nodes, false);
    439     }
    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         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         Collection nodes = fetchNodeList(prefix);
    473         if (nodes.isEmpty())
    474         {
    475             return new HierarchicalConfiguration();
    476         }
    477
    478         final HierarchicalConfiguration parent = this;
    479         HierarchicalConfiguration result = new HierarchicalConfiguration()
    480         {
    481             // Override interpolate to always interpolate on the parent
    482             protected Object interpolate(Object value)
    483             {
    484                 return parent.interpolate(value);
    485             }
    486         };
    487         CloneVisitor visitor = new CloneVisitor();
    488
    489         // Initialize the new root node
    490         Object value = null;
    491         int valueCount = 0;
    492         for (Iterator it = nodes.iterator(); it.hasNext();)
    493         {
    494             ConfigurationNode nd = (ConfigurationNode) it.next();
    495             if (nd.getValue() != null)
    496             {
    497                 value = nd.getValue();
    498                 valueCount++;
    499             }
    500             nd.visit(visitor);
    501
    502             for (Iterator it2 = visitor.getClone().getChildren().iterator(); it2
    503                     .hasNext();)
    504             {
    505                 result.getRootNode().addChild((ConfigurationNode) it2.next());
    506             }
    507             for (Iterator it2 = visitor.getClone().getAttributes().iterator(); it2
    508                     .hasNext();)
    509             {
    510                 result.getRootNode().addAttribute(
    511                         (ConfigurationNode) it2.next());
    512             }
    513         }
    514
    515         // Determine the value of the new root
    516         if (valueCount == 1)
    517         {
    518             result.getRootNode().setValue(value);
    519         }
    520         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         List nodes = fetchNodeList(key);
    574         if (nodes.size() != 1)
    575         {
    576             throw new IllegalArgumentException(
    577                     "Passed in key must select exactly one node: " + key);
    578         }
    579         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         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         List nodes = fetchNodeList(key);
    629         List configs = new ArrayList(nodes.size());
    630         for (Iterator it = nodes.iterator(); it.hasNext();)
    631         {
    632             configs.add(createSubnodeConfiguration((ConfigurationNode) it.next()));
    633         }
    634         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         SubnodeConfiguration result = new SubnodeConfiguration(this, node);
    649         registerSubnodeConfiguration(result);
    650         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         SubnodeConfiguration result = createSubnodeConfiguration(node);
    667         result.setSubnodeKey(subnodeKey);
    668         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         fireEvent(EVENT_SUBNODE_CHANGED, null, event, event.isBeforeUpdate());
    683     }
    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         config.addConfigurationListener(new ConfigurationListener()
    696         {
    697             public void configurationChanged(ConfigurationEvent event)
    698             {
    699                 subnodeConfigurationChanged(event);
    700             }
    701         });
    702     }
    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         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         fireEvent(EVENT_SET_PROPERTY, key, value, true);
    728
    729         // Update the existing nodes for this property
    730         Iterator itNodes = fetchNodeList(key).iterator();
    731         Iterator itValues;
    732         if (!isDelimiterParsingDisabled())
    733         {
    734             itValues = PropertyConverter.toIterator(value, getListDelimiter());
    735         }
    736         else
    737         {
    738             itValues = new SingletonIterator(value);
    739         }
    740
    741         while (itNodes.hasNext() && itValues.hasNext())
    742         {
    743             ((ConfigurationNode) itNodes.next()).setValue(itValues.next());
    744         }
    745
    746         // Add additional nodes if necessary
    747         while (itValues.hasNext())
    748         {
    749             addPropertyDirect(key, itValues.next());
    750         }
    751
    752         // Remove remaining nodes
    753         while (itNodes.hasNext())
    754         {
    755             clearNode((ConfigurationNode) itNodes.next());
    756         }
    757
    758         fireEvent(EVENT_SET_PROPERTY, key, value, false);
    759     }
    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         fireEvent(EVENT_CLEAR_TREE, key, null, true);
    772         List nodes = fetchNodeList(key);
    773
    774         for (Iterator it = nodes.iterator(); it.hasNext();)
    775         {
    776             removeNode((ConfigurationNode) it.next());
    777         }
    778         fireEvent(EVENT_CLEAR_TREE, key, nodes, false);
    779     }
    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         fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
    791         List nodes = fetchNodeList(key);
    792
    793         for (Iterator it = nodes.iterator(); it.hasNext();)
    794         {
    795             clearNode((ConfigurationNode) it.next());
    796         }
    797
    798         fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
    799     }
    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         DefinedKeysVisitor visitor = new DefinedKeysVisitor();
    811         getRootNode().visit(visitor);
    812
    813         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         DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
    827         if (containsKey(prefix))
    828         {
    829             // explicitly add the prefix
    830             visitor.getKeyList().add(prefix);
    831         }
    832
    833         List nodes = fetchNodeList(prefix);
    834
    835         for (Iterator itNodes = nodes.iterator(); itNodes.hasNext();)
    836         {
    837             ConfigurationNode node = (ConfigurationNode) itNodes.next();
    838             for (Iterator it = node.getChildren().iterator(); it.hasNext();)
    839             {
    840                 ((ConfigurationNode) it.next()).visit(visitor);
    841             }
    842             for (Iterator it = node.getAttributes().iterator(); it.hasNext();)
    843             {
    844                 ((ConfigurationNode) it.next()).visit(visitor);
    845             }
    846         }
    847
    848         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         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             HierarchicalConfiguration copy = (HierarchicalConfiguration) super
    878                     .clone();
    879
    880             // clone the nodes, too
    881             CloneVisitor v = new CloneVisitor();
    882             getRootNode().visit(v);
    883             copy.setRootNode(v.getClone());
    884
    885             return copy;
    886         }
    887         catch (CloneNotSupportedException cex)
    888         {
    889             // should not happen
    890             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         HierarchicalConfiguration c = (HierarchicalConfiguration) clone();
    907         c.getRootNode().visit(new ConfigurationNodeVisitorAdapter()
    908         {
    909             public void visitAfterChildren(ConfigurationNode node)
    910             {
    911                 node.setValue(interpolate(node.getValue()));
    912             }
    913         });
    914         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         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     }
    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         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         DefinedVisitor visitor = new DefinedVisitor();
    969         node.visit(visitor);
    970         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         removeNode((ConfigurationNode) node);
    985     }
    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         ConfigurationNode parent = node.getParentNode();
    997         if (parent != null)
    998         {
    999             parent.removeChild(node);
    1000             if (!nodeDefined(parent))
    1001             {
    1002                 removeNode(parent);
    1003             }
    1004         }
    1005     }
    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         clearNode((ConfigurationNode) node);
    1018     }
    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         node.setValue(null);
    1029         if (!nodeDefined(node))
    1030         {
    1031             removeNode(node);
    1032         }
    1033     }
    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         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         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         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         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         ConfigurationNode node = data.getParent();
    1117
    1118         // Create missing nodes on the path
    1119         for (Iterator it = data.getPathNodes().iterator(); it.hasNext();)
    1120         {
    1121             ConfigurationNode child = createNode((String) it.next());
    1122             node.addChild(child);
    1123             node = child;
    1124         }
    1125
    1126         // Add new target node
    1127         ConfigurationNode child = createNode(data.getNewNodeName());
    1128         if (data.isAttribute())
    1129         {
    1130             node.addAttribute(child);
    1131         }
    1132         else
    1133         {
    1134             node.addChild(child);
    1135         }
    1136         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         node.visit(new ConfigurationNodeVisitorAdapter()
    1153         {
    1154             public void visitBeforeChildren(ConfigurationNode node)
    1155             {
    1156                 node.setReference(null);
    1157             }
    1158         });
    1159     }
    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             super();
    1181         }
    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             super(name);
    1191         }
    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             super(name, value);
    1202         }
    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             this(src.getName(), src.getValue());
    1214             setReference(src.getReference());
    1215             for (Iterator it = src.getChildren().iterator(); it.hasNext();)
    1216             {
    1217                 ConfigurationNode nd = (ConfigurationNode) it.next();
    1218                 // Don't change the parent node
    1219                 ConfigurationNode parent = nd.getParentNode();
    1220                 addChild(nd);
    1221                 nd.setParentNode(parent);
    1222             }
    1223
    1224             for (Iterator it = src.getAttributes().iterator(); it.hasNext();)
    1225             {
    1226                 ConfigurationNode nd = (ConfigurationNode) it.next();
    1227                 // Don't change the parent node
    1228                 ConfigurationNode parent = nd.getParentNode();
    1229                 addAttribute(nd);
    1230                 nd.setParentNode(parent);
    1231             }
    1232         }
    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             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             setParentNode(node);
    1252         }
    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             addChild((ConfigurationNode) node);
    1262         }
    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             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             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             boolean childrenRemoved = removeChild(name);
    1294             boolean attrsRemoved = removeAttribute(name);
    1295             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             int length = 0;
    1312             if (key != null)
    1313             {
    1314                 length = key.length();
    1315                 if (getName() != null)
    1316                 {
    1317                     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             visitor.visitBeforeChildren(this, key);
    1330
    1331             for (Iterator it = getChildren().iterator(); it.hasNext()
    1332                     && !visitor.terminate();)
    1333             {
    1334                 ((Node) it.next()).visit(visitor, key);
    1335             }
    1336             for (Iterator it = getAttributes().iterator(); it.hasNext()
    1337                     && !visitor.terminate();)
    1338             {
    1339                 ((Node) it.next()).visit(visitor, key);
    1340             }
    1341
    1342             if (key != null)
    1343             {
    1344                 key.setLength(length);
    1345             }
    1346             visitor.visitAfterChildren(this, key);
    1347         }
    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     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         }
    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         }
    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             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     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             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             defined = node.getValue() != null;
    1430         }
    1431
    1432         /***
    1433          * Returns the defined flag.
    1434          *
    1435          * @return the defined flag
    1436          */
    1437         public boolean isDefined()
    1438         {
    1439             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         {
    1460             keyList = new ListOrderedSet();
    1461             parentKeys = new Stack();
    1462         }
    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             this();
    1473             parentKeys.push(prefix);
    1474         }
    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             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             parentKeys.pop();
    1495         }
    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             String parentKey = parentKeys.isEmpty() ? null
    1506                     : (String) parentKeys.peek();
    1507             String key = getExpressionEngine().nodeKey(node, parentKey);
    1508             parentKeys.push(key);
    1509             if (node.getValue() != null)
    1510             {
    1511                 keyList.add(key);
    1512             }
    1513         }
    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         {
    1533             copyStack = new Stack();
    1534         }
    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             ConfigurationNode copy = (ConfigurationNode) copyStack.pop();
    1544             if (copyStack.isEmpty())
    1545             {
    1546                 result = copy;
    1547             }
    1548         }
    1549
    1550         /***
    1551          * Visits and copies the specified node.
    1552          *
    1553          * @param node the node
    1554          */
    1555         public void visitBeforeChildren(ConfigurationNode node)
    1556         {
    1557             ConfigurationNode copy = (ConfigurationNode) node.clone();
    1558             copy.setParentNode(null);
    1559
    1560             if (!copyStack.isEmpty())
    1561             {
    1562                 if (node.isAttribute())
    1563                 {
    1564                     ((ConfigurationNode) copyStack.peek()).addAttribute(copy);
    1565                 }
    1566                 else
    1567                 {
    1568                     ((ConfigurationNode) copyStack.peek()).addChild(copy);
    1569                 }
    1570             }
    1571
    1572             copyStack.push(copy);
    1573         }
    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             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     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             Collection subNodes = new LinkedList(node.getChildren());
    1613             subNodes.addAll(node.getAttributes());
    1614             Iterator children = subNodes.iterator();
    1615             Node sibling1 = null;
    1616             Node nd = null;
    1617
    1618             while (children.hasNext())
    1619             {
    1620                 // find the next new node
    1621                 do
    1622                 {
    1623                     sibling1 = nd;
    1624                     nd = (Node) children.next();
    1625                 } while (nd.getReference() != null && children.hasNext());
    1626
    1627                 if (nd.getReference() == null)
    1628                 {
    1629                     // find all following new nodes
    1630                     List newNodes = new LinkedList();
    1631                     newNodes.add(nd);
    1632                     while (children.hasNext())
    1633                     {
    1634                         nd = (Node) children.next();
    1635                         if (nd.getReference() == null)
    1636                         {
    1637                             newNodes.add(nd);
    1638                         }
    1639                         else
    1640                         {
    1641                             break;
    1642                         }
    1643                     }
    1644
    1645                     // Insert all new nodes
    1646                     Node sibling2 = (nd.getReference() == null) ? null : nd;
    1647                     for (Iterator it = newNodes.iterator(); it.hasNext();)
    1648                     {
    1649                         Node insertNode = (Node) it.next();
    1650                         if (insertNode.getReference() == null)
    1651                         {
    1652                             Object ref = insert(insertNode, node, sibling1, sibling2);
    1653                             if (ref != null)
    1654                             {
    1655                                 insertNode.setReference(ref);
    1656                             }
    1657                             sibling1 = insertNode;
    1658                         }
    1659                     }
    1660                 }
    1661             }
    1662         }
    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 }