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