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.Iterator;
24  import java.util.LinkedList;
25  import java.util.List;
26  import java.util.Set;
27  import java.util.Stack;
28  
29  import org.apache.commons.collections.set.ListOrderedSet;
30  import org.apache.commons.collections.iterators.SingletonIterator;
31  import org.apache.commons.configuration.tree.ConfigurationNode;
32  import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
33  import org.apache.commons.configuration.tree.DefaultConfigurationNode;
34  import org.apache.commons.configuration.tree.DefaultExpressionEngine;
35  import org.apache.commons.configuration.tree.ExpressionEngine;
36  import org.apache.commons.configuration.tree.NodeAddData;
37  import org.apache.commons.lang.StringUtils;
38  
39  /***
40   * <p>A specialized configuration class that extends its base class by the
41   * ability of keeping more structure in the stored properties.</p><p>There
42   * are some sources of configuration data that cannot be stored very well in a
43   * <code>BaseConfiguration</code> object because then their structure is lost.
44   * This is especially true for XML documents. This class can deal with such
45   * structured configuration sources by storing the properties in a tree-like
46   * organization.</p><p>The internal used storage form allows for a more
47   * sophisticated access to single properties. As an example consider the
48   * following XML document:</p><p>
49   *
50   * <pre>
51   * &lt;database&gt;
52   *   &lt;tables&gt;
53   *     &lt;table&gt;
54   *       &lt;name&gt;users&lt;/name&gt;
55   *       &lt;fields&gt;
56   *         &lt;field&gt;
57   *           &lt;name&gt;lid&lt;/name&gt;
58   *           &lt;type&gt;long&lt;/name&gt;
59   *         &lt;/field&gt;
60   *         &lt;field&gt;
61   *           &lt;name&gt;usrName&lt;/name&gt;
62   *           &lt;type&gt;java.lang.String&lt;/type&gt;
63   *         &lt;/field&gt;
64   *        ...
65   *       &lt;/fields&gt;
66   *     &lt;/table&gt;
67   *     &lt;table&gt;
68   *       &lt;name&gt;documents&lt;/name&gt;
69   *       &lt;fields&gt;
70   *         &lt;field&gt;
71   *           &lt;name&gt;docid&lt;/name&gt;
72   *           &lt;type&gt;long&lt;/type&gt;
73   *         &lt;/field&gt;
74   *         ...
75   *       &lt;/fields&gt;
76   *     &lt;/table&gt;
77   *     ...
78   *   &lt;/tables&gt;
79   * &lt;/database&gt;
80   * </pre>
81   *
82   * </p><p>If this document is parsed and stored in a
83   * <code>HierarchicalConfiguration</code> object (which can be done by one of
84   * the sub classes), there are enhanced possibilities of accessing properties.
85   * The keys for querying information can contain indices that select a certain
86   * element if there are multiple hits.</p><p>For instance the key
87   * <code>tables.table(0).name</code> can be used to find out the name of the
88   * first table. In opposite <code>tables.table.name</code> would return a
89   * collection with the names of all available tables. Similarily the key
90   * <code>tables.table(1).fields.field.name</code> returns a collection with
91   * the names of all fields of the second table. If another index is added after
92   * the <code>field</code> element, a single field can be accessed:
93   * <code>tables.table(1).fields.field(0).name</code>.</p><p>There is a
94   * <code>getMaxIndex()</code> method that returns the maximum allowed index
95   * that can be added to a given property key. This method can be used to iterate
96   * over all values defined for a certain property.</p>
97   *
98   * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger </a>
99   * @version $Id: HierarchicalConfiguration.java,v 1.14 2004/12/02 22:05:52
100  * ebourg Exp $
101  */
102 public class HierarchicalConfiguration extends AbstractConfiguration implements Serializable, Cloneable
103 {
104     /*** Constant for the clear tree event.*/
105     public static final int EVENT_CLEAR_TREE = 10;
106 
107     /*** Constant for the add nodes event.*/
108     public static final int EVENT_ADD_NODES = 11;
109 
110     /***
111      * The serial version UID.
112      */
113     private static final long serialVersionUID = 3373812230395363192L;
114 
115     /*** Stores the default expression engine to be used for new objects.*/
116     private static ExpressionEngine defaultExpressionEngine = new DefaultExpressionEngine();
117 
118     /*** Stores the root node of this configuration. This field is required for
119      * backwards compatibility only.
120      */
121     private Node root;
122 
123     /*** Stores the root configuration node.*/
124     private ConfigurationNode rootNode;
125 
126     /*** Stores the expression engine for this instance.*/
127     private ExpressionEngine expressionEngine;
128 
129     /***
130      * Creates a new instance of <code>HierarchicalConfiguration</code>.
131      */
132     public HierarchicalConfiguration()
133     {
134         setRootNode(new Node());
135     }
136 
137     /***
138      * Returns the root node of this hierarchical configuration. This method
139      * exists for backwards compatibility only. New code should use the
140      * <code>{@link #getRootNode()}</code> method instead, which operates on
141      * the preferred data type <code>ConfigurationNode</code>.
142      *
143      * @return the root node
144      */
145     public Node getRoot()
146     {
147         return root;
148     }
149 
150     /***
151      * Sets the root node of this hierarchical configuration. This method
152      * exists for backwards compatibility only. New code should use the
153      * <code>{@link #setRootNode(ConfigurationNode)}</code> method instead,
154      * which operates on the preferred data type <code>ConfigurationNode</code>.
155      *
156      * @param node the root node
157      */
158     public void setRoot(Node node)
159     {
160         if (node == null)
161         {
162             throw new IllegalArgumentException("Root node must not be null!");
163         }
164         root = node;
165         rootNode = null;
166     }
167 
168     /***
169      * Returns the root node of this hierarchical configuration.
170      *
171      * @return the root node
172      * @since 1.3
173      */
174     public ConfigurationNode getRootNode()
175     {
176         return (rootNode != null) ? rootNode : root;
177     }
178 
179     /***
180      * Sets the root node of this hierarchical configuration.
181      *
182      * @param rootNode the root node
183      * @since 1.3
184      */
185     public void setRootNode(ConfigurationNode rootNode)
186     {
187         if (rootNode == null)
188         {
189             throw new IllegalArgumentException("Root node must not be null!");
190         }
191         this.rootNode = rootNode;
192 
193         // For backward compatibility also set the old root field.
194         root = (rootNode instanceof Node) ? (Node) rootNode : new Node(rootNode);
195     }
196 
197     /***
198      * Returns the default expression engine.
199      *
200      * @return the default expression engine
201      * @since 1.3
202      */
203     public static ExpressionEngine getDefaultExpressionEngine()
204     {
205         return defaultExpressionEngine;
206     }
207 
208     /***
209      * Sets the default expression engine. This expression engine will be used
210      * if no specific engine was set for an instance. It is shared between all
211      * hierarchical configuration instances. So modifying its properties will
212      * impact all instances, for which no specific engine is set.
213      *
214      * @param engine the new default expression engine
215      * @since 1.3
216      */
217     public static void setDefaultExpressionEngine(ExpressionEngine engine)
218     {
219         if (engine == null)
220         {
221             throw new IllegalArgumentException(
222                     "Default expression engine must not be null!");
223         }
224         defaultExpressionEngine = engine;
225     }
226 
227     /***
228      * Returns the expression engine used by this configuration. This method
229      * will never return <b>null</b>; if no specific expression engine was set,
230      * the default expression engine will be returned.
231      *
232      * @return the current expression engine
233      * @since 1.3
234      */
235     public ExpressionEngine getExpressionEngine()
236     {
237         return (expressionEngine != null) ? expressionEngine
238                 : getDefaultExpressionEngine();
239     }
240 
241     /***
242      * Sets the expression engine to be used by this configuration. All property
243      * keys this configuration has to deal with will be interpreted by this
244      * engine.
245      *
246      * @param expressionEngine the new expression engine; can be <b>null</b>,
247      * then the default expression engine will be used
248      * @since 1.3
249      */
250     public void setExpressionEngine(ExpressionEngine expressionEngine)
251     {
252         this.expressionEngine = expressionEngine;
253     }
254 
255     /***
256      * Fetches the specified property. This task is delegated to the associated
257      * expression engine.
258      *
259      * @param key the key to be looked up
260      * @return the found value
261      */
262     public Object getProperty(String key)
263     {
264         List nodes = fetchNodeList(key);
265 
266         if (nodes.size() == 0)
267         {
268             return null;
269         }
270         else
271         {
272             List list = new ArrayList();
273             for (Iterator it = nodes.iterator(); it.hasNext();)
274             {
275                 ConfigurationNode node = (ConfigurationNode) it.next();
276                 if (node.getValue() != null)
277                 {
278                     list.add(node.getValue());
279                 }
280             }
281 
282             if (list.size() < 1)
283             {
284                 return null;
285             }
286             else
287             {
288                 return (list.size() == 1) ? list.get(0) : list;
289             }
290         }
291     }
292 
293     /***
294      * Adds the property with the specified key. This task will be delegated to
295      * the associated <code>ExpressionEngine</code>, so the passed in key
296      * must match the requirements of this implementation.
297      *
298      * @param key the key of the new property
299      * @param obj the value of the new property
300      */
301     protected void addPropertyDirect(String key, Object obj)
302     {
303         NodeAddData data = getExpressionEngine().prepareAdd(getRootNode(), key);
304         ConfigurationNode node = processNodeAddData(data);
305         node.setValue(obj);
306     }
307 
308     /***
309      * Adds a collection of nodes at the specified position of the configuration
310      * tree. This method works similar to <code>addProperty()</code>, but
311      * instead of a single property a whole collection of nodes can be added -
312      * and thus complete configuration sub trees. E.g. with this method it is
313      * possible to add parts of another <code>HierarchicalConfiguration</code>
314      * object to this object. If the passed in key refers to an existing and
315      * unique node, the new nodes are added to this node. Otherwise a new node
316      * will be created at the specified position in the hierarchy.
317      *
318      * @param key the key where the nodes are to be added; can be <b>null </b>,
319      * then they are added to the root node
320      * @param nodes a collection with the <code>Node</code> objects to be
321      * added
322      */
323     public void addNodes(String key, Collection nodes)
324     {
325         if (nodes == null || nodes.isEmpty())
326         {
327             return;
328         }
329 
330         fireEvent(EVENT_ADD_NODES, key, nodes, true);
331         ConfigurationNode parent;
332         List target = fetchNodeList(key);
333         if (target.size() == 1)
334         {
335             // existing unique key
336             parent = (ConfigurationNode) target.get(0);
337         }
338         else
339         {
340             // otherwise perform an add operation
341             parent = processNodeAddData(getExpressionEngine().prepareAdd(
342                     getRootNode(), key));
343         }
344 
345         if (parent.isAttribute())
346         {
347             throw new IllegalArgumentException(
348                     "Cannot add nodes to an attribute node!");
349         }
350         for (Iterator it = nodes.iterator(); it.hasNext();)
351         {
352             ConfigurationNode child = (ConfigurationNode) it.next();
353             if (child.isAttribute())
354             {
355                 parent.addAttribute(child);
356             }
357             else
358             {
359                 parent.addChild(child);
360             }
361         }
362         fireEvent(EVENT_ADD_NODES, key, nodes, false);
363     }
364 
365     /***
366      * Checks if this configuration is empty. Empty means that there are no keys
367      * with any values, though there can be some (empty) nodes.
368      *
369      * @return a flag if this configuration is empty
370      */
371     public boolean isEmpty()
372     {
373         return !nodeDefined(getRootNode());
374     }
375 
376     /***
377      * Creates a new <code>Configuration</code> object containing all keys
378      * that start with the specified prefix. This implementation will return a
379      * <code>HierarchicalConfiguration</code> object so that the structure of
380      * the keys will be saved.
381      *
382      * @param prefix the prefix of the keys for the subset
383      * @return a new configuration object representing the selected subset
384      */
385     public Configuration subset(String prefix)
386     {
387         Collection nodes = fetchNodeList(prefix);
388         if (nodes.isEmpty())
389         {
390             return new HierarchicalConfiguration();
391         }
392 
393         HierarchicalConfiguration result = new HierarchicalConfiguration();
394         CloneVisitor visitor = new CloneVisitor();
395 
396         for (Iterator it = nodes.iterator(); it.hasNext();)
397         {
398             ConfigurationNode nd = (ConfigurationNode) it.next();
399             nd.visit(visitor);
400 
401             for (Iterator it2 = visitor.getClone().getChildren().iterator(); it2.hasNext();)
402             {
403                 result.getRootNode().addChild((ConfigurationNode) it2.next());
404             }
405             for (Iterator it2 = visitor.getClone().getAttributes().iterator(); it2.hasNext();)
406             {
407                 result.getRootNode().addAttribute((ConfigurationNode) it2.next());
408             }
409         }
410 
411         return (result.isEmpty()) ? new HierarchicalConfiguration() : result;
412     }
413 
414     /***
415      * <p>
416      * Returns a hierarchical subnode configuration object that wraps the
417      * configuration node specified by the given key. This method provides an
418      * easy means of accessing sub trees of a hierarchical configuration. In the
419      * returned configuration the sub tree can directly be accessed, it becomes
420      * the root node of this configuration. Because of this the passed in key
421      * must select exactly one configuration node; otherwise an
422      * <code>IllegalArgumentException</code> will be thrown.
423      * </p>
424      * <p>
425      * The difference between this method and the
426      * <code>{@link #subset(String)}</code> method is that
427      * <code>subset()</code> supports arbitrary subsets of configuration nodes
428      * while <code>configurationAt()</code> only returns a single sub tree.
429      * Please refer to the documentation of the
430      * <code>SubnodeConfiguration</code> class to obtain further information
431      * about subnode configurations and when they should be used.
432      * </p>
433      *
434      * @param key the key that selects the sub tree
435      * @return a hierarchical configuration that contains this sub tree
436      * @see SubnodeConfiguration
437      * @since 1.3
438      */
439     public SubnodeConfiguration configurationAt(String key)
440     {
441         List nodes = fetchNodeList(key);
442         if (nodes.size() != 1)
443         {
444             throw new IllegalArgumentException(
445                     "Passed in key must select exactly one node: " + key);
446         }
447         return createSubnodeConfiguration((ConfigurationNode) nodes.get(0));
448     }
449 
450     /***
451      * Returns a list of sub configurations for all configuration nodes selected
452      * by the given key. This method will evaluate the passed in key (using the
453      * current <code>ExpressionEngine</code>) and then create a subnode
454      * configuration for each returned node (like
455      * <code>{@link #configurationAt(String)}</code>}). This is especially
456      * useful when dealing with list-like structures. As an example consider the
457      * configuration that contains data about database tables and their fields.
458      * If you need access to all fields of a certain table, you can simply do
459      *
460      * <pre>
461      * List fields = config.configurationsAt("tables.table(0).fields.field");
462      * for(Iterator it = fields.iterator(); it.hasNext();)
463      * {
464      *     HierarchicalConfiguration sub = (HierarchicalConfiguration) it.next();
465      *     // now the children and attributes of the field node can be
466      *     // directly accessed
467      *     String fieldName = sub.getString("name");
468      *     String fieldType = sub.getString("type");
469      *     ...
470      * </pre>
471      *
472      * @param key the key for selecting the desired nodes
473      * @return a list with hierarchical configuration objects; each
474      * configuration represents one of the nodes selected by the passed in key
475      * @since 1.3
476      */
477     public List configurationsAt(String key)
478     {
479         List nodes = fetchNodeList(key);
480         List configs = new ArrayList(nodes.size());
481         for (Iterator it = nodes.iterator(); it.hasNext();)
482         {
483             configs.add(createSubnodeConfiguration((ConfigurationNode) it.next()));
484         }
485         return configs;
486     }
487 
488     /***
489      * Creates a subnode configuration for the specified node. This method is
490      * called by <code>configurationAt()</code> and
491      * <code>configurationsAt()</code>.
492      *
493      * @param node the node, for which a subnode configuration is to be created
494      * @return the configuration for the given node
495      * @since 1.3
496      */
497     protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node)
498     {
499         return new SubnodeConfiguration(this, node);
500     }
501 
502     /***
503      * Checks if the specified key is contained in this configuration. Note that
504      * for this configuration the term &quot;contained&quot; means that the key
505      * has an associated value. If there is a node for this key that has no
506      * value but children (either defined or undefined), this method will still
507      * return <b>false </b>.
508      *
509      * @param key the key to be chekced
510      * @return a flag if this key is contained in this configuration
511      */
512     public boolean containsKey(String key)
513     {
514         return getProperty(key) != null;
515     }
516 
517     /***
518      * Sets the value of the specified property.
519      *
520      * @param key the key of the property to set
521      * @param value the new value of this property
522      */
523     public void setProperty(String key, Object value)
524     {
525         fireEvent(EVENT_SET_PROPERTY, key, value, true);
526 
527         Iterator itNodes = fetchNodeList(key).iterator();
528         Iterator itValues;
529         if (!isDelimiterParsingDisabled())
530         {
531             itValues = PropertyConverter.toIterator(value, getListDelimiter());
532         }
533         else
534         {
535             itValues = new SingletonIterator(value);
536         }
537         while (itNodes.hasNext() && itValues.hasNext())
538         {
539             ((ConfigurationNode) itNodes.next()).setValue(itValues.next());
540         }
541 
542         // Add additional nodes if necessary
543         while (itValues.hasNext())
544         {
545             addPropertyDirect(key, itValues.next());
546         }
547 
548         // Remove remaining nodes
549         while (itNodes.hasNext())
550         {
551             clearNode((ConfigurationNode) itNodes.next());
552         }
553 
554         fireEvent(EVENT_SET_PROPERTY, key, value, false);
555     }
556 
557     /***
558      * Removes all values of the property with the given name and of keys that
559      * start with this name. So if there is a property with the key
560      * &quot;foo&quot; and a property with the key &quot;foo.bar&quot;, a call
561      * of <code>clearTree("foo")</code> would remove both properties.
562      *
563      * @param key the key of the property to be removed
564      */
565     public void clearTree(String key)
566     {
567         fireEvent(EVENT_CLEAR_TREE, key, null, true);
568         List nodes = fetchNodeList(key);
569 
570         for (Iterator it = nodes.iterator(); it.hasNext();)
571         {
572             removeNode((ConfigurationNode) it.next());
573         }
574         fireEvent(EVENT_CLEAR_TREE, key, nodes, false);
575     }
576 
577     /***
578      * Removes the property with the given key. Properties with names that start
579      * with the given key (i.e. properties below the specified key in the
580      * hierarchy) won't be affected.
581      *
582      * @param key the key of the property to be removed
583      */
584     public void clearProperty(String key)
585     {
586         fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
587         List nodes = fetchNodeList(key);
588 
589         for (Iterator it = nodes.iterator(); it.hasNext();)
590         {
591             clearNode((ConfigurationNode) it.next());
592         }
593 
594         fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
595     }
596 
597     /***
598      * Returns an iterator with all keys defined in this configuration.
599      * Note that the keys returned by this method will not contain any
600      * indices. This means that some structure will be lost.</p>
601      *
602      * @return an iterator with the defined keys in this configuration
603      */
604     public Iterator getKeys()
605     {
606         DefinedKeysVisitor visitor = new DefinedKeysVisitor();
607         getRootNode().visit(visitor);
608 
609         return visitor.getKeyList().iterator();
610     }
611 
612     /***
613      * Returns an iterator with all keys defined in this configuration that
614      * start with the given prefix. The returned keys will not contain any
615      * indices.
616      *
617      * @param prefix the prefix of the keys to start with
618      * @return an iterator with the found keys
619      */
620     public Iterator getKeys(String prefix)
621     {
622         DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
623         List nodes = fetchNodeList(prefix);
624 
625         for (Iterator itNodes = nodes.iterator(); itNodes.hasNext();)
626         {
627             ConfigurationNode node = (ConfigurationNode) itNodes.next();
628             for (Iterator it = node.getChildren().iterator(); it.hasNext();)
629             {
630                 ((ConfigurationNode) it.next()).visit(visitor);
631             }
632             for (Iterator it = node.getAttributes().iterator(); it.hasNext();)
633             {
634                 ((ConfigurationNode) it.next()).visit(visitor);
635             }
636         }
637 
638         return visitor.getKeyList().iterator();
639     }
640 
641     /***
642      * Returns the maximum defined index for the given key. This is useful if
643      * there are multiple values for this key. They can then be addressed
644      * separately by specifying indices from 0 to the return value of this
645      * method.
646      *
647      * @param key the key to be checked
648      * @return the maximum defined index for this key
649      */
650     public int getMaxIndex(String key)
651     {
652         return fetchNodeList(key).size() - 1;
653     }
654 
655     /***
656      * Creates a copy of this object. This new configuration object will contain
657      * copies of all nodes in the same structure. Registered event listeners
658      * won't be cloned; so they are not registered at the returned copy.
659      *
660      * @return the copy
661      * @since 1.2
662      */
663     public Object clone()
664     {
665         try
666         {
667             HierarchicalConfiguration copy = (HierarchicalConfiguration) super
668                     .clone();
669 
670             // clone the nodes, too
671             CloneVisitor v = new CloneVisitor();
672             getRootNode().visit(v);
673             copy.setRootNode(v.getClone());
674             copy.clearConfigurationListeners();
675 
676             return copy;
677         }
678         catch (CloneNotSupportedException cex)
679         {
680             // should not happen
681             throw new ConfigurationRuntimeException(cex);
682         }
683     }
684 
685     /***
686      * Helper method for fetching a list of all nodes that are addressed by the
687      * specified key.
688      *
689      * @param key the key
690      * @return a list with all affected nodes (never <b>null </b>)
691      */
692     protected List fetchNodeList(String key)
693     {
694         return getExpressionEngine().query(getRootNode(), key);
695     }
696 
697     /***
698      * Recursive helper method for fetching a property. This method processes
699      * all facets of a configuration key, traverses the tree of properties and
700      * fetches the the nodes of all matching properties.
701      *
702      * @param keyPart the configuration key iterator
703      * @param node the actual node
704      * @param nodes here the found nodes are stored
705      * @deprecated Property keys are now evaluated by the expression engine
706      * associated with the configuration; this method will no longer be called.
707      * If you want to modify the way properties are looked up, consider
708      * implementing you own <code>ExpressionEngine</code> implementation.
709      */
710     protected void findPropertyNodes(ConfigurationKey.KeyIterator keyPart,
711             Node node, Collection nodes)
712     {
713     }
714 
715     /***
716      * Checks if the specified node is defined.
717      *
718      * @param node the node to be checked
719      * @return a flag if this node is defined
720      * @deprecated Use the method <code>{@link #nodeDefined(ConfigurationNode)}</code>
721      * instead.
722      */
723     protected boolean nodeDefined(Node node)
724     {
725         return nodeDefined((ConfigurationNode) node);
726     }
727 
728     /***
729      * Checks if the specified node is defined.
730      *
731      * @param node the node to be checked
732      * @return a flag if this node is defined
733      */
734     protected boolean nodeDefined(ConfigurationNode node)
735     {
736         DefinedVisitor visitor = new DefinedVisitor();
737         node.visit(visitor);
738         return visitor.isDefined();
739     }
740 
741     /***
742      * Removes the specified node from this configuration. This method ensures
743      * that parent nodes that become undefined by this operation are also
744      * removed.
745      *
746      * @param node the node to be removed
747      * @deprecated Use the method <code>{@link #removeNode(ConfigurationNode)}</code>
748      * instead.
749      */
750     protected void removeNode(Node node)
751     {
752         removeNode((ConfigurationNode) node);
753     }
754 
755     /***
756      * Removes the specified node from this configuration. This method ensures
757      * that parent nodes that become undefined by this operation are also
758      * removed.
759      *
760      * @param node the node to be removed
761      */
762     protected void removeNode(ConfigurationNode node)
763     {
764         ConfigurationNode parent = node.getParentNode();
765         if (parent != null)
766         {
767             parent.removeChild(node);
768             if (!nodeDefined(parent))
769             {
770                 removeNode(parent);
771             }
772         }
773     }
774 
775     /***
776      * Clears the value of the specified node. If the node becomes undefined by
777      * this operation, it is removed from the hierarchy.
778      *
779      * @param node the node to be cleard
780      * @deprecated Use the method <code>{@link #clearNode(ConfigurationNode)}</code>
781      * instead
782      */
783     protected void clearNode(Node node)
784     {
785         clearNode((ConfigurationNode) node);
786     }
787 
788     /***
789      * Clears the value of the specified node. If the node becomes undefined by
790      * this operation, it is removed from the hierarchy.
791      *
792      * @param node the node to be cleard
793      */
794     protected void clearNode(ConfigurationNode node)
795     {
796         node.setValue(null);
797         if (!nodeDefined(node))
798         {
799             removeNode(node);
800         }
801     }
802 
803     /***
804      * Returns a reference to the parent node of an add operation. Nodes for new
805      * properties can be added as children of this node. If the path for the
806      * specified key does not exist so far, it is created now.
807      *
808      * @param keyIt the iterator for the key of the new property
809      * @param startNode the node to start the search with
810      * @return the parent node for the add operation
811      * @deprecated Adding new properties is now to a major part delegated to the
812      * <code>ExpressionEngine</code> associated with this configuration instance.
813      * This method will no longer be called. Developers who want to modify the
814      * process of adding new properties should consider implementing their own
815      * expression engine.
816      */
817     protected Node fetchAddNode(ConfigurationKey.KeyIterator keyIt, Node startNode)
818     {
819         return null;
820     }
821 
822     /***
823      * Finds the last existing node for an add operation. This method traverses
824      * the configuration tree along the specified key. The last existing node on
825      * this path is returned.
826      *
827      * @param keyIt the key iterator
828      * @param node the actual node
829      * @return the last existing node on the given path
830      * @deprecated Adding new properties is now to a major part delegated to the
831      * <code>ExpressionEngine</code> associated with this configuration instance.
832      * This method will no longer be called. Developers who want to modify the
833      * process of adding new properties should consider implementing their own
834      * expression engine.
835      */
836     protected Node findLastPathNode(ConfigurationKey.KeyIterator keyIt, Node node)
837     {
838         return null;
839     }
840 
841     /***
842      * Creates the missing nodes for adding a new property. This method ensures
843      * that there are corresponding nodes for all components of the specified
844      * configuration key.
845      *
846      * @param keyIt the key iterator
847      * @param root the base node of the path to be created
848      * @return the last node of the path
849      * @deprecated Adding new properties is now to a major part delegated to the
850      * <code>ExpressionEngine</code> associated with this configuration instance.
851      * This method will no longer be called. Developers who want to modify the
852      * process of adding new properties should consider implementing their own
853      * expression engine.
854      */
855     protected Node createAddPath(ConfigurationKey.KeyIterator keyIt, Node root)
856     {
857         return null;
858     }
859 
860     /***
861      * Creates a new <code>Node</code> object with the specified name. This
862      * method can be overloaded in derived classes if a specific node type is
863      * needed. This base implementation always returns a new object of the
864      * <code>Node</code> class.
865      *
866      * @param name the name of the new node
867      * @return the new node
868      */
869     protected Node createNode(String name)
870     {
871         return new Node(name);
872     }
873 
874     /***
875      * Helper method for processing a node add data object obtained from the
876      * expression engine. This method will create all new nodes.
877      *
878      * @param data the data object
879      * @return the new node
880      * @since 1.3
881      */
882     private ConfigurationNode processNodeAddData(NodeAddData data)
883     {
884         ConfigurationNode node = data.getParent();
885 
886         // Create missing nodes on the path
887         for (Iterator it = data.getPathNodes().iterator(); it.hasNext();)
888         {
889             ConfigurationNode child = createNode((String) it.next());
890             node.addChild(child);
891             node = child;
892         }
893 
894         // Add new target node
895         ConfigurationNode child = createNode(data.getNewNodeName());
896         if (data.isAttribute())
897         {
898             node.addAttribute(child);
899         }
900         else
901         {
902             node.addChild(child);
903         }
904         return child;
905     }
906 
907     /***
908      * A data class for storing (hierarchical) property information. A property
909      * can have a value and an arbitrary number of child properties. From version 1.3 on this class
910      * is only a thin wrapper over the
911      * <code>{@link org.apache.commons.configuration.tree.DefaultConfigurationNode DefaultconfigurationNode}</code>
912      * class that exists mainly for the purpose of backwards compatibility.
913      */
914     public static class Node extends DefaultConfigurationNode implements Serializable
915     {
916         /***
917          * The serial version UID.
918          */
919         private static final long serialVersionUID = -6357500633536941775L;
920 
921         /***
922          * Creates a new instance of <code>Node</code>.
923          */
924         public Node()
925         {
926             super();
927         }
928 
929         /***
930          * Creates a new instance of <code>Node</code> and sets the name.
931          *
932          * @param name the node's name
933          */
934         public Node(String name)
935         {
936             super(name);
937         }
938 
939         /***
940          * Creates a new instance of <code>Node</code> and sets the name and the value.
941          *
942          * @param name the node's name
943          * @param value the value
944          */
945         public Node(String name, Object value)
946         {
947             super(name, value);
948         }
949 
950         /***
951          * Creates a new instance of <code>Node</code> based on the given
952          * source node. All properties of the source node, including its
953          * children and attributes, will be copied.
954          *
955          * @param src the node to be copied
956          */
957         public Node(ConfigurationNode src)
958         {
959             this(src.getName(), src.getValue());
960             setReference(src.getReference());
961             for (Iterator it = src.getChildren().iterator(); it.hasNext();)
962             {
963                 addChild((ConfigurationNode) it.next());
964             }
965             for (Iterator it = src.getAttributes().iterator(); it.hasNext();)
966             {
967                 addAttribute((ConfigurationNode) it.next());
968             }
969         }
970 
971         /***
972          * Returns the parent of this node.
973          *
974          * @return this node's parent (can be <b>null</b>)
975          */
976         public Node getParent()
977         {
978             return (Node) getParentNode();
979         }
980 
981         /***
982          * Sets the parent of this node.
983          *
984          * @param node the parent node
985          */
986         public void setParent(Node node)
987         {
988             setParentNode(node);
989         }
990 
991         /***
992          * Adds the given node to the children of this node.
993          *
994          * @param node the child to be added
995          */
996         public void addChild(Node node)
997         {
998             addChild((ConfigurationNode) node);
999         }
1000 
1001         /***
1002          * Returns a flag whether this node has child elements.
1003          *
1004          * @return <b>true</b> if there is a child node, <b>false</b> otherwise
1005          */
1006         public boolean hasChildren()
1007         {
1008             return getChildrenCount() > 0 || getAttributeCount() > 0;
1009         }
1010 
1011         /***
1012          * Removes the specified child from this node.
1013          *
1014          * @param child the child node to be removed
1015          * @return a flag if the child could be found
1016          */
1017         public boolean remove(Node child)
1018         {
1019             return child.isAttribute() ? removeAttribute(child) : removeChild(child);
1020         }
1021 
1022         /***
1023          * Removes all children with the given name.
1024          *
1025          * @param name the name of the children to be removed
1026          * @return a flag if children with this name existed
1027          */
1028         public boolean remove(String name)
1029         {
1030             boolean childrenRemoved = removeChild(name);
1031             boolean attrsRemoved = removeAttribute(name);
1032             return childrenRemoved || attrsRemoved;
1033         }
1034 
1035         /***
1036          * A generic method for traversing this node and all of its children.
1037          * This method sends the passed in visitor to this node and all of its
1038          * children.
1039          *
1040          * @param visitor the visitor
1041          * @param key here a configuration key with the name of the root node of
1042          * the iteration can be passed; if this key is not <b>null </b>, the
1043          * full pathes to the visited nodes are builded and passed to the
1044          * visitor's <code>visit()</code> methods
1045          */
1046         public void visit(NodeVisitor visitor, ConfigurationKey key)
1047         {
1048             int length = 0;
1049             if (key != null)
1050             {
1051                 length = key.length();
1052                 if (getName() != null)
1053                 {
1054                     key
1055                             .append(StringUtils
1056                                     .replace(
1057                                             isAttribute() ? ConfigurationKey
1058                                                     .constructAttributeKey(getName())
1059                                                     : getName(),
1060                                             String
1061                                                     .valueOf(ConfigurationKey.PROPERTY_DELIMITER),
1062                                             ConfigurationKey.ESCAPED_DELIMITER));
1063                 }
1064             }
1065 
1066             visitor.visitBeforeChildren(this, key);
1067 
1068             for (Iterator it = getChildren().iterator(); it.hasNext()
1069                     && !visitor.terminate();)
1070             {
1071                 ((Node) it.next()).visit(visitor, key);
1072             }
1073             for (Iterator it = getAttributes().iterator(); it.hasNext()
1074                     && !visitor.terminate();)
1075             {
1076                 ((Node) it.next()).visit(visitor, key);
1077             }
1078 
1079             if (key != null)
1080             {
1081                 key.setLength(length);
1082             }
1083             visitor.visitAfterChildren(this, key);
1084         }
1085     }
1086 
1087     /***
1088      * <p>Definition of a visitor class for traversing a node and all of its
1089      * children.</p><p>This class defines the interface of a visitor for
1090      * <code>Node</code> objects and provides a default implementation. The
1091      * method <code>visit()</code> of <code>Node</code> implements a generic
1092      * iteration algorithm based on the <em>Visitor</em> pattern. By providing
1093      * different implementations of visitors it is possible to collect different
1094      * data during the iteration process.</p>
1095      *
1096      */
1097     public static class NodeVisitor
1098     {
1099         /***
1100          * Visits the specified node. This method is called during iteration for
1101          * each node before its children have been visited.
1102          *
1103          * @param node the actual node
1104          * @param key the key of this node (may be <b>null </b>)
1105          */
1106         public void visitBeforeChildren(Node node, ConfigurationKey key)
1107         {
1108         }
1109 
1110         /***
1111          * Visits the specified node after its children have been processed.
1112          * This gives a visitor the opportunity of collecting additional data
1113          * after the child nodes have been visited.
1114          *
1115          * @param node the node to be visited
1116          * @param key the key of this node (may be <b>null </b>)
1117          */
1118         public void visitAfterChildren(Node node, ConfigurationKey key)
1119         {
1120         }
1121 
1122         /***
1123          * Returns a flag that indicates if iteration should be stopped. This
1124          * method is called after each visited node. It can be useful for
1125          * visitors that search a specific node. If this node is found, the
1126          * whole process can be stopped. This base implementation always returns
1127          * <b>false </b>.
1128          *
1129          * @return a flag if iteration should be stopped
1130          */
1131         public boolean terminate()
1132         {
1133             return false;
1134         }
1135     }
1136 
1137     /***
1138      * A specialized visitor that checks if a node is defined.
1139      * &quot;Defined&quot; in this terms means that the node or at least one of
1140      * its sub nodes is associated with a value.
1141      *
1142      */
1143     static class DefinedVisitor extends ConfigurationNodeVisitorAdapter
1144     {
1145         /*** Stores the defined flag. */
1146         private boolean defined;
1147 
1148         /***
1149          * Checks if iteration should be stopped. This can be done if the first
1150          * defined node is found.
1151          *
1152          * @return a flag if iteration should be stopped
1153          */
1154         public boolean terminate()
1155         {
1156             return isDefined();
1157         }
1158 
1159         /***
1160          * Visits the node. Checks if a value is defined.
1161          *
1162          * @param node the actual node
1163          */
1164         public void visitBeforeChildren(ConfigurationNode node)
1165         {
1166             defined = node.getValue() != null;
1167         }
1168 
1169         /***
1170          * Returns the defined flag.
1171          *
1172          * @return the defined flag
1173          */
1174         public boolean isDefined()
1175         {
1176             return defined;
1177         }
1178     }
1179 
1180     /***
1181      * A specialized visitor that fills a list with keys that are defined in a
1182      * node hierarchy.
1183      */
1184     class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter
1185     {
1186         /*** Stores the list to be filled. */
1187         private Set keyList;
1188 
1189         /*** A stack with the keys of the already processed nodes. */
1190         private Stack parentKeys;
1191 
1192         /***
1193          * Default constructor.
1194          */
1195         public DefinedKeysVisitor()
1196         {
1197             keyList = new ListOrderedSet();
1198             parentKeys = new Stack();
1199         }
1200 
1201         /***
1202          * Creates a new <code>DefinedKeysVisitor</code> instance and sets the
1203          * prefix for the keys to fetch.
1204          *
1205          * @param prefix the prefix
1206          */
1207         public DefinedKeysVisitor(String prefix)
1208         {
1209             this();
1210             parentKeys.push(prefix);
1211         }
1212 
1213         /***
1214          * Returns the list with all defined keys.
1215          *
1216          * @return the list with the defined keys
1217          */
1218         public Set getKeyList()
1219         {
1220             return keyList;
1221         }
1222 
1223         /***
1224          * Visits the node after its children has been processed. Removes this
1225          * node's key from the stack.
1226          *
1227          * @param node the node
1228          */
1229         public void visitAfterChildren(ConfigurationNode node)
1230         {
1231             parentKeys.pop();
1232         }
1233 
1234         /***
1235          * Visits the specified node. If this node has a value, its key is added
1236          * to the internal list.
1237          *
1238          * @param node the node to be visited
1239          */
1240         public void visitBeforeChildren(ConfigurationNode node)
1241         {
1242             String parentKey = parentKeys.isEmpty() ? null
1243                     : (String) parentKeys.peek();
1244             String key = getExpressionEngine().nodeKey(node, parentKey);
1245             parentKeys.push(key);
1246             if (node.getValue() != null)
1247             {
1248                 keyList.add(key);
1249             }
1250         }
1251     }
1252 
1253     /***
1254      * A specialized visitor that is able to create a deep copy of a node
1255      * hierarchy.
1256      */
1257     static class CloneVisitor extends ConfigurationNodeVisitorAdapter
1258     {
1259         /*** A stack with the actual object to be copied. */
1260         private Stack copyStack;
1261 
1262         /*** Stores the result of the clone process. */
1263         private ConfigurationNode result;
1264 
1265         /***
1266          * Creates a new instance of <code>CloneVisitor</code>.
1267          */
1268         public CloneVisitor()
1269         {
1270             copyStack = new Stack();
1271         }
1272 
1273         /***
1274          * Visits the specified node after its children have been processed.
1275          *
1276          * @param node the node
1277          */
1278         public void visitAfterChildren(ConfigurationNode node)
1279         {
1280             ConfigurationNode copy = (ConfigurationNode) copyStack.pop();
1281             if (copyStack.isEmpty())
1282             {
1283                 result = copy;
1284             }
1285         }
1286 
1287         /***
1288          * Visits and copies the specified node.
1289          *
1290          * @param node the node
1291          */
1292         public void visitBeforeChildren(ConfigurationNode node)
1293         {
1294             ConfigurationNode copy = (ConfigurationNode) node.clone();
1295             copy.setParentNode(null);
1296 
1297             if (!copyStack.isEmpty())
1298             {
1299                 if (node.isAttribute())
1300                 {
1301                     ((ConfigurationNode) copyStack.peek()).addAttribute(copy);
1302                 }
1303                 else
1304                 {
1305                     ((ConfigurationNode) copyStack.peek()).addChild(copy);
1306                 }
1307             }
1308 
1309             copyStack.push(copy);
1310         }
1311 
1312         /***
1313          * Returns the result of the clone process. This is the root node of the
1314          * cloned node hierarchy.
1315          *
1316          * @return the cloned root node
1317          */
1318         public ConfigurationNode getClone()
1319         {
1320             return result;
1321         }
1322     }
1323 
1324     /***
1325      * A specialized visitor base class that can be used for storing the tree of
1326      * configuration nodes. The basic idea is that each node can be associated
1327      * with a reference object. This reference object has a concrete meaning in
1328      * a derived class, e.g. an entry in a JNDI context or an XML element. When
1329      * the configuration tree is set up, the <code>load()</code> method is
1330      * responsible for setting the reference objects. When the configuration
1331      * tree is later modified, new nodes do not have a defined reference object.
1332      * This visitor class processes all nodes and finds the ones without a
1333      * defined reference object. For those nodes the <code>insert()</code>
1334      * method is called, which must be defined in concrete sub classes. This
1335      * method can perform all steps to integrate the new node into the original
1336      * structure.
1337      *
1338      */
1339     protected abstract static class BuilderVisitor extends NodeVisitor
1340     {
1341         /***
1342          * Visits the specified node before its children have been traversed.
1343          *
1344          * @param node the node to visit
1345          * @param key the current key
1346          */
1347         public void visitBeforeChildren(Node node, ConfigurationKey key)
1348         {
1349             Collection subNodes = new LinkedList(node.getChildren());
1350             subNodes.addAll(node.getAttributes());
1351             Iterator children = subNodes.iterator();
1352             Node sibling1 = null;
1353             Node nd = null;
1354 
1355             while (children.hasNext())
1356             {
1357                 // find the next new node
1358                 do
1359                 {
1360                     sibling1 = nd;
1361                     nd = (Node) children.next();
1362                 } while (nd.getReference() != null && children.hasNext());
1363 
1364                 if (nd.getReference() == null)
1365                 {
1366                     // find all following new nodes
1367                     List newNodes = new LinkedList();
1368                     newNodes.add(nd);
1369                     while (children.hasNext())
1370                     {
1371                         nd = (Node) children.next();
1372                         if (nd.getReference() == null)
1373                         {
1374                             newNodes.add(nd);
1375                         }
1376                         else
1377                         {
1378                             break;
1379                         }
1380                     }
1381 
1382                     // Insert all new nodes
1383                     Node sibling2 = (nd.getReference() == null) ? null : nd;
1384                     for (Iterator it = newNodes.iterator(); it.hasNext();)
1385                     {
1386                         Node insertNode = (Node) it.next();
1387                         if (insertNode.getReference() == null)
1388                         {
1389                             Object ref = insert(insertNode, node, sibling1, sibling2);
1390                             if (ref != null)
1391                             {
1392                                 insertNode.setReference(ref);
1393                             }
1394                             sibling1 = insertNode;
1395                         }
1396                     }
1397                 }
1398             }
1399         }
1400 
1401         /***
1402          * Inserts a new node into the structure constructed by this builder.
1403          * This method is called for each node that has been added to the
1404          * configuration tree after the configuration has been loaded from its
1405          * source. These new nodes have to be inserted into the original
1406          * structure. The passed in nodes define the position of the node to be
1407          * inserted: its parent and the siblings between to insert. The return
1408          * value is interpreted as the new reference of the affected
1409          * <code>Node</code> object; if it is not <b>null </b>, it is passed
1410          * to the node's <code>setReference()</code> method.
1411          *
1412          * @param newNode the node to be inserted
1413          * @param parent the parent node
1414          * @param sibling1 the sibling after which the node is to be inserted;
1415          * can be <b>null </b> if the new node is going to be the first child
1416          * node
1417          * @param sibling2 the sibling before which the node is to be inserted;
1418          * can be <b>null </b> if the new node is going to be the last child
1419          * node
1420          * @return the reference object for the node to be inserted
1421          */
1422         protected abstract Object insert(Node newNode, Node parent, Node sibling1, Node sibling2);
1423     }
1424 }