View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration;
19  
20  import java.io.Serializable;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.LinkedHashSet;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Set;
29  import java.util.Stack;
30  
31  import org.apache.commons.configuration.event.ConfigurationEvent;
32  import org.apache.commons.configuration.event.ConfigurationListener;
33  import org.apache.commons.configuration.tree.ConfigurationNode;
34  import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
35  import org.apache.commons.configuration.tree.DefaultConfigurationNode;
36  import org.apache.commons.configuration.tree.DefaultExpressionEngine;
37  import org.apache.commons.configuration.tree.ExpressionEngine;
38  import org.apache.commons.configuration.tree.NodeAddData;
39  import org.apache.commons.configuration.tree.ViewNode;
40  import org.apache.commons.lang.StringUtils;
41  
42  /**
43   * <p>A specialized configuration class that extends its base class by the
44   * ability of keeping more structure in the stored properties.</p><p>There
45   * are some sources of configuration data that cannot be stored very well in a
46   * {@code BaseConfiguration} object because then their structure is lost.
47   * This is especially true for XML documents. This class can deal with such
48   * structured configuration sources by storing the properties in a tree-like
49   * organization.</p><p>The internal used storage form allows for a more
50   * sophisticated access to single properties. As an example consider the
51   * following XML document:</p><p>
52   *
53   * <pre>
54   * &lt;database&gt;
55   *   &lt;tables&gt;
56   *     &lt;table&gt;
57   *       &lt;name&gt;users&lt;/name&gt;
58   *       &lt;fields&gt;
59   *         &lt;field&gt;
60   *           &lt;name&gt;lid&lt;/name&gt;
61   *           &lt;type&gt;long&lt;/name&gt;
62   *         &lt;/field&gt;
63   *         &lt;field&gt;
64   *           &lt;name&gt;usrName&lt;/name&gt;
65   *           &lt;type&gt;java.lang.String&lt;/type&gt;
66   *         &lt;/field&gt;
67   *        ...
68   *       &lt;/fields&gt;
69   *     &lt;/table&gt;
70   *     &lt;table&gt;
71   *       &lt;name&gt;documents&lt;/name&gt;
72   *       &lt;fields&gt;
73   *         &lt;field&gt;
74   *           &lt;name&gt;docid&lt;/name&gt;
75   *           &lt;type&gt;long&lt;/type&gt;
76   *         &lt;/field&gt;
77   *         ...
78   *       &lt;/fields&gt;
79   *     &lt;/table&gt;
80   *     ...
81   *   &lt;/tables&gt;
82   * &lt;/database&gt;
83   * </pre>
84   *
85   * </p><p>If this document is parsed and stored in a
86   * {@code HierarchicalConfiguration} object (which can be done by one of
87   * the sub classes), there are enhanced possibilities of accessing properties.
88   * The keys for querying information can contain indices that select a certain
89   * element if there are multiple hits.</p><p>For instance the key
90   * {@code tables.table(0).name} can be used to find out the name of the
91   * first table. In opposite {@code tables.table.name} would return a
92   * collection with the names of all available tables. Similarly the key
93   * {@code tables.table(1).fields.field.name} returns a collection with
94   * the names of all fields of the second table. If another index is added after
95   * the {@code field} element, a single field can be accessed:
96   * {@code tables.table(1).fields.field(0).name}.</p><p>There is a
97   * {@code getMaxIndex()} method that returns the maximum allowed index
98   * that can be added to a given property key. This method can be used to iterate
99   * over all values defined for a certain property.</p>
100  * <p>Since the 1.3 release of <em>Commons Configuration</em> hierarchical
101  * configurations support an <em>expression engine</em>. This expression engine
102  * is responsible for evaluating the passed in configuration keys and map them
103  * to the stored properties. The examples above are valid for the default
104  * expression engine, which is used when a new {@code HierarchicalConfiguration}
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 1330666 2012-04-26 06:12:30Z 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() || !(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 }