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
018package org.apache.commons.configuration;
019
020import java.io.Serializable;
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.Iterator;
025import java.util.LinkedHashSet;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Set;
029import java.util.Stack;
030
031import org.apache.commons.configuration.event.ConfigurationEvent;
032import org.apache.commons.configuration.event.ConfigurationListener;
033import org.apache.commons.configuration.tree.ConfigurationNode;
034import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
035import org.apache.commons.configuration.tree.DefaultConfigurationNode;
036import org.apache.commons.configuration.tree.DefaultExpressionEngine;
037import org.apache.commons.configuration.tree.ExpressionEngine;
038import org.apache.commons.configuration.tree.NodeAddData;
039import org.apache.commons.configuration.tree.ViewNode;
040import 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.NodeVisitor.html 901729 2014-03-15 20:24:09Z oheger $
128 */
129public 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() || !(value instanceof String))
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            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(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}