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