View Javadoc

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