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