View Javadoc

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