001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.configuration;
019    
020    import java.io.Serializable;
021    import java.util.ArrayList;
022    import java.util.Collection;
023    import java.util.Iterator;
024    import java.util.LinkedList;
025    import java.util.List;
026    import java.util.Set;
027    import java.util.Stack;
028    
029    import org.apache.commons.collections.iterators.SingletonIterator;
030    import org.apache.commons.collections.set.ListOrderedSet;
031    import org.apache.commons.configuration.event.ConfigurationEvent;
032    import org.apache.commons.configuration.event.ConfigurationListener;
033    import org.apache.commons.configuration.tree.ConfigurationNode;
034    import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
035    import org.apache.commons.configuration.tree.DefaultConfigurationNode;
036    import org.apache.commons.configuration.tree.DefaultExpressionEngine;
037    import org.apache.commons.configuration.tree.ExpressionEngine;
038    import org.apache.commons.configuration.tree.NodeAddData;
039    import org.apache.commons.lang.StringUtils;
040    
041    /**
042     * <p>A specialized configuration class that extends its base class by the
043     * ability of keeping more structure in the stored properties.</p><p>There
044     * are some sources of configuration data that cannot be stored very well in a
045     * <code>BaseConfiguration</code> object because then their structure is lost.
046     * This is especially true for XML documents. This class can deal with such
047     * structured configuration sources by storing the properties in a tree-like
048     * organization.</p><p>The internal used storage form allows for a more
049     * sophisticated access to single properties. As an example consider the
050     * following XML document:</p><p>
051     *
052     * <pre>
053     * &lt;database&gt;
054     *   &lt;tables&gt;
055     *     &lt;table&gt;
056     *       &lt;name&gt;users&lt;/name&gt;
057     *       &lt;fields&gt;
058     *         &lt;field&gt;
059     *           &lt;name&gt;lid&lt;/name&gt;
060     *           &lt;type&gt;long&lt;/name&gt;
061     *         &lt;/field&gt;
062     *         &lt;field&gt;
063     *           &lt;name&gt;usrName&lt;/name&gt;
064     *           &lt;type&gt;java.lang.String&lt;/type&gt;
065     *         &lt;/field&gt;
066     *        ...
067     *       &lt;/fields&gt;
068     *     &lt;/table&gt;
069     *     &lt;table&gt;
070     *       &lt;name&gt;documents&lt;/name&gt;
071     *       &lt;fields&gt;
072     *         &lt;field&gt;
073     *           &lt;name&gt;docid&lt;/name&gt;
074     *           &lt;type&gt;long&lt;/type&gt;
075     *         &lt;/field&gt;
076     *         ...
077     *       &lt;/fields&gt;
078     *     &lt;/table&gt;
079     *     ...
080     *   &lt;/tables&gt;
081     * &lt;/database&gt;
082     * </pre>
083     *
084     * </p><p>If this document is parsed and stored in a
085     * <code>HierarchicalConfiguration</code> object (which can be done by one of
086     * the sub classes), there are enhanced possibilities of accessing properties.
087     * The keys for querying information can contain indices that select a certain
088     * element if there are multiple hits.</p><p>For instance the key
089     * <code>tables.table(0).name</code> can be used to find out the name of the
090     * first table. In opposite <code>tables.table.name</code> would return a
091     * collection with the names of all available tables. Similarly the key
092     * <code>tables.table(1).fields.field.name</code> returns a collection with
093     * the names of all fields of the second table. If another index is added after
094     * the <code>field</code> element, a single field can be accessed:
095     * <code>tables.table(1).fields.field(0).name</code>.</p><p>There is a
096     * <code>getMaxIndex()</code> method that returns the maximum allowed index
097     * that can be added to a given property key. This method can be used to iterate
098     * over all values defined for a certain property.</p>
099     * <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    }