View Javadoc

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