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 }