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