KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > myvietnam > mvncore > configuration > HierarchicalConfiguration


1 package net.myvietnam.mvncore.configuration;
2
3 /* ====================================================================
4  * The Apache Software License, Version 1.1
5  *
6  * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
7  * reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if
22  * any, must include the following acknowledgement:
23  * "This product includes software developed by the
24  * Apache Software Foundation (http://www.apache.org/)."
25  * Alternately, this acknowledgement may appear in the software itself,
26  * if and wherever such third-party acknowledgements normally appear.
27  *
28  * 4. The names "The Jakarta Project", "Commons", and "Apache Software
29  * Foundation" must not be used to endorse or promote products derived
30  * from this software without prior written permission. For written
31  * permission, please contact apache@apache.org.
32  *
33  * 5. Products derived from this software may not be called "Apache"
34  * nor may "Apache" appear in their names without prior written
35  * permission of the Apache Software Foundation.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals on behalf of the Apache Software Foundation. For more
53  * information on the Apache Software Foundation, please see
54  * <http://www.apache.org/>.
55  */

56
57 import java.io.Serializable JavaDoc;
58 import java.util.ArrayList JavaDoc;
59 import java.util.Collection JavaDoc;
60 import java.util.HashSet JavaDoc;
61 import java.util.Iterator JavaDoc;
62 import java.util.LinkedList JavaDoc;
63 import java.util.List JavaDoc;
64 import java.util.Map JavaDoc;
65 import java.util.Set JavaDoc;
66 import java.util.Stack JavaDoc;
67
68 import org.apache.commons.collections.SequencedHashMap;
69 import org.apache.commons.lang.StringUtils;
70
71 /**
72  * <p>A specialized configuration class that extends its base class by the
73  * ability of keeping more structure in the stored properties.</p>
74  * <p>There are some sources of configuration data that cannot be stored
75  * very well in a <code>BaseConfiguration</code> object because then their
76  * structure is lost. This is especially true for XML documents. This class
77  * can deal with such structured configuration sources by storing the
78  * properties in a tree-like organization.</p>
79  * <p>The internal used storage form allows for a more sophisticated access to
80  * single properties. As an example consider the following XML document:</p>
81  * <p><pre>
82  * &lt;database&gt;
83  * &lt;tables&gt;
84  * &lt;table&gt;
85  * &lt;name&gt;users&lt;/name&gt;
86  * &lt;fields&gt;
87  * &lt;field&gt;
88  * &lt;name&gt;lid&lt;/name&gt;
89  * &lt;type&gt;long&lt;/name&gt;
90  * &lt;/field&gt;
91  * &lt;field&gt;
92  * &lt;name&gt;usrName&lt;/name&gt;
93  * &lt;type&gt;java.lang.String&lt;/type&gt;
94  * &lt;/field&gt;
95  * ...
96  * &lt;/fields&gt;
97  * &lt;/table&gt;
98  * &lt;table&gt;
99  * &lt;name&gt;documents&lt;/name&gt;
100  * &lt;fields&gt;
101  * &lt;field&gt;
102  * &lt;name&gt;docid&lt;/name&gt;
103  * &lt;type&gt;long&lt;/type&gt;
104  * &lt;/field&gt;
105  * ...
106  * &lt;/fields&gt;
107  * &lt;/table&gt;
108  * ...
109  * &lt;/tables&gt;
110  * &lt;/database&gt;
111  * </pre></p>
112  * <p>If this document is parsed and stored in a
113  * <code>HierarchicalConfiguration</code> object (which can be done by one of
114  * the sub classes), there are enhanced possibilities of accessing properties.
115  * The keys for querying information can contain indices that select a certain
116  * element if there are multiple hits.</p>
117  * <p>For instance the key <code>tables.table(0).name</code> can be used to
118  * find out the name of the first table. In opposite
119  * <code>tables.table.name</code> would return a collection with the names of
120  * all available tables. Similarily the key
121  * <code>tables.table(1).fields.field.name</code> returns a collection with the
122  * names of all fields of the second table. If another index is added after the
123  * <code>field</code> element, a single field can be accessed:
124  * <code>tables.table(1).fields.field(0).name</code>.</p>
125  * <p>There is a <code>getMaxIndex()</code> method that returns the maximum
126  * allowed index that can be added to a given property key. This method can be
127  * used to iterate over all values defined for a certain property.</p>
128  *
129  * @author <a HREF="mailto:oliver.heger@t-online.de">Oliver Heger</a>
130  * @version $Id: HierarchicalConfiguration.java,v 1.1 2003/12/09 08:25:30 huumai Exp $
131  */

132 public class HierarchicalConfiguration extends AbstractConfiguration
133 {
134     /** Constant for a new dummy key.*/
135     private static final String JavaDoc NEW_KEY = "newKey";
136
137     /** Stores the root node of this configuration.*/
138     private Node root = new Node();
139
140     /**
141      * Creates a new instance of <code>HierarchicalConfiguration</code>.
142      */

143     public HierarchicalConfiguration()
144     {
145         super();
146     }
147
148     /**
149      * Creates a new instance of <code>HierarchicalConfiguration</code>
150      * and initializes it with default properties.
151      * @param defaults default properties to be used
152      */

153     public HierarchicalConfiguration(Configuration defaults)
154     {
155         super(defaults);
156     }
157
158     /**
159      * Returns the root node of this hierarchical configuration.
160      * @return the root node
161      */

162     public Node getRoot()
163     {
164         return root;
165     }
166
167     /**
168      * Sets the root node of this hierarchical configuration.
169      * @param node the root node
170      */

171     public void setRoot(Node node)
172     {
173         if (node == null)
174         {
175             throw new IllegalArgumentException JavaDoc("Root node must not be null!");
176         } /* if */
177         root = node;
178     }
179
180     /**
181      * Fetches the specified property. Performs a recursive lookup in the
182      * tree with the configuration properties.
183      * @param key the key to be looked up
184      * @return the found value
185      */

186     protected Object JavaDoc getPropertyDirect(String JavaDoc key)
187     {
188         List JavaDoc nodes = fetchNodeList(key);
189
190         if (nodes.size() == 0)
191         {
192             return null;
193         } /* if */
194         else
195         {
196             Container cont = new Container();
197             for (Iterator JavaDoc it = nodes.iterator(); it.hasNext();)
198             {
199                 Node nd = (Node) it.next();
200                 if (nd.getValue() != null)
201                 {
202                     cont.add(nd.getValue());
203                 } /* if */
204             } /* for */
205
206             if (cont.size() < 1)
207             {
208                 return null;
209             } /* if */
210             else
211             {
212                 return (cont.size() == 1) ? cont.get(0) : cont;
213             } /* else */
214         } /* else */
215     }
216
217     /**
218      * <p>Adds the property with the specified key.</p>
219      * <p>To be able to deal with the structure supported by this configuration
220      * implementation the passed in key is of importance, especially the
221      * indices it might contain. The following example should clearify this:
222      * Suppose the actual configuration contains the following elements:</p>
223      * <p><pre>
224      * tables
225      * +-- table
226      * +-- name = user
227      * +-- fields
228      * +-- field
229      * +-- name = uid
230      * +-- field
231      * +-- name = firstName
232      * ...
233      * +-- table
234      * +-- name = documents
235      * +-- fields
236      * ...
237      * </pre></p>
238      * <p>In this example a database structure is defined, e.g. all fields of
239      * the first table could be accessed using the key
240      * <code>tables.table(0).fields.field.name</code>. If now properties are
241      * to be added, it must be exactly specified at which position in the
242      * hierarchy the new property is to be inserted. So to add a new field name
243      * to a table it is not enough to say just</p>
244      * <p><pre>
245      * config.addProperty("tables.table.fields.field.name", "newField");
246      * </pre></p>
247      * <p>The statement given above contains some ambiguity. For instance
248      * it is not clear, to which table the new field should be added. If this
249      * method finds such an ambiguity, it is resolved by following the last
250      * valid path. Here this would be the last table. The same is true for the
251      * <code>field</code>; because there are multiple fields and no explicit
252      * index is provided, a new <code>name</code> property would be
253      * added to the last field - which is propably not what was desired.</p>
254      * <p>To make things clear explicit indices should be provided whenever
255      * possible. In the example above the exact table could be specified by
256      * providing an index for the <code>table</code> element as in
257      * <code>tables.table(1).fields</code>. By specifying an index it can also
258      * be expressed that at a given position in the configuration tree a new
259      * branch should be added. In the example above we did not want to add
260      * an additional <code>name</code> element to the last field of the table,
261      * but we want a complete new <code>field</code> element. This can be
262      * achieved by specifying an invalid index (like -1) after the element
263      * where a new branch should be created. Given this our example would run:
264      * </p><p><pre>
265      * config.addProperty("tables.table(1).fields.field(-1).name", "newField");
266      * </pre></p>
267      * <p>With this notation it is possible to add new branches everywhere.
268      * We could for instance create a new <code>table</code> element by
269      * specifying</p>
270      * <p><pre>
271      * config.addProperty("tables.table(-1).fields.field.name", "newField2");
272      * </pre></p>
273      * <p>(Note that because after the <code>table</code> element a new
274      * branch is created indices in following elements are not relevant; the
275      * branch is new so there cannot be any ambiguities.)</p>
276      * @param key the key of the new property
277      * @param obj the value of the new property
278      */

279     protected void addPropertyDirect(String JavaDoc key, Object JavaDoc obj)
280     {
281         ConfigurationKey.KeyIterator it = new ConfigurationKey(key).iterator();
282         Node parent = fetchAddNode(it, getRoot());
283
284         Node child = new Node(it.currentKey(true));
285         child.setValue(obj);
286         parent.addChild(child);
287     }
288
289     /**
290      * Adds a collection of nodes at the specified position of the
291      * configuration tree. This method works similar to
292      * <code>addProperty()</code>, but instead of a single property a whole
293      * collection of nodes can be added - and thus complete configuration
294      * sub trees. E.g. with this method it is possible to add parts of
295      * another <code>HierarchicalConfiguration</code> object to this object.
296      * @param key the key where the nodes are to be added; can be <b>null</b>,
297      * then they are added to the root node
298      * @param nodes a collection with the <code>Node</code> objects to be
299      * added
300      */

301     public void addNodes(String JavaDoc key, Collection JavaDoc nodes)
302     {
303         if (nodes == null || nodes.isEmpty())
304         {
305             return;
306         } /* if */
307
308         Node parent;
309         if (StringUtils.isEmpty(key))
310         {
311             parent = getRoot();
312         } /* if */
313         else
314         {
315             ConfigurationKey.KeyIterator kit =
316                 new ConfigurationKey(key).iterator();
317             parent = fetchAddNode(kit, getRoot());
318
319             // fetchAddNode() does not really fetch the last component,
320
// but one before. So we must perform an additional step.
321
ConfigurationKey keyNew =
322                 new ConfigurationKey(kit.currentKey(true));
323             keyNew.append(NEW_KEY);
324             parent = fetchAddNode(keyNew.iterator(), parent);
325         } /* else */
326
327         for (Iterator JavaDoc it = nodes.iterator(); it.hasNext();)
328         {
329             parent.addChild((Node) it.next());
330         } /* for */
331     }
332
333     /**
334      * Checks if this configuration is empty. Empty means that there are
335      * no keys with any values, though there can be some (empty) nodes.
336      * @return a flag if this configuration is empty
337      */

338     public boolean isEmpty()
339     {
340         return !nodeDefined(getRoot());
341     }
342
343     /**
344      * Checks if the specified key is contained in this configuration.
345      * Note that for this configuration the term &quot;contained&quot; means
346      * that the key has an associated value. If there is a node for this key
347      * that has no value but children (either defined or undefined), this
348      * method will still return <b>false</b>.
349      * @param key the key to be chekced
350      * @return a flag if this key is contained in this configuration
351      */

352     public boolean containsKey(String JavaDoc key)
353     {
354         return getPropertyDirect(key) != null;
355     }
356
357     /**
358      * Removes all values of the property with the given name.
359      * @param key the key of the property to be removed
360      */

361     public void clearProperty(String JavaDoc key)
362     {
363         List JavaDoc nodes = fetchNodeList(key);
364
365         for (Iterator JavaDoc it = nodes.iterator(); it.hasNext();)
366         {
367             removeNode((Node) it.next());
368         } /* for */
369     }
370
371     /**
372      * <p>Returns an iterator with all keys defined in this configuration.</p>
373      * <p>Note that the keys returned by this method will not contain
374      * any indices. This means that some structure will be lost.</p>
375      * @return an iterator with the defined keys in this configuration
376      */

377     public Iterator JavaDoc getKeys()
378     {
379         DefinedKeysVisitor visitor = new DefinedKeysVisitor();
380         getRoot().visit(visitor, new ConfigurationKey());
381         return visitor.getKeyList().iterator();
382     }
383
384     /**
385      * Creates a new <code>Configuration</code> object containing all keys
386      * that start with the specified prefix. This implementation will return
387      * a <code>HierarchicalConfiguration</code> object so that the structure
388      * of the keys will be saved.
389      * @param prefix the prefix of the keys for the subset
390      * @return a new configuration object representing the selected subset
391      */

392     public Configuration subset(String JavaDoc prefix)
393     {
394         Collection JavaDoc nodes = fetchNodeList(prefix);
395         if (nodes.isEmpty())
396         {
397             return null;
398         } /* if */
399
400         HierarchicalConfiguration result = new HierarchicalConfiguration();
401         CloneVisitor visitor = new CloneVisitor();
402
403         for (Iterator JavaDoc it = nodes.iterator(); it.hasNext();)
404         {
405             Node nd = (Node) it.next();
406             nd.visit(visitor, null);
407
408             Container children = visitor.getClone().getChildren();
409             if (children.size() > 0)
410             {
411                 for (int i = 0; i < children.size(); i++)
412                 {
413                     result.getRoot().addChild((Node) children.get(i));
414                 } /* for */
415             } /* if */
416             else
417             {
418                 // In this case we cannot shorten the key because only
419
// values are found without further child nodes.
420
result.getRoot().addChild(visitor.getClone());
421             } /* else */
422         } /* for */
423
424         return (result.isEmpty()) ? null : result;
425     }
426
427     /**
428      * Returns the maximum defined index for the given key. This is
429      * useful if there are multiple values for this key. They can then be
430      * addressed separately by specifying indices from 0 to the return value
431      * of this method.
432      * @param key the key to be checked
433      * @return the maximum defined index for this key
434      */

435     public int getMaxIndex(String JavaDoc key)
436     {
437         return fetchNodeList(key).size() - 1;
438     }
439
440     /**
441      * Helper method for fetching a list of all nodes that are addressed by
442      * the specified key.
443      * @param key the key
444      * @return a list with all affected nodes (never <b>null</b>)
445      */

446     protected List JavaDoc fetchNodeList(String JavaDoc key)
447     {
448         List JavaDoc nodes = new LinkedList JavaDoc();
449         findPropertyNodes(
450             new ConfigurationKey(key).iterator(),
451             getRoot(),
452             nodes);
453         return nodes;
454     }
455
456     /**
457      * Recursive helper method for fetching a property. This method
458      * processes all facets of a configuration key, traverses the tree of
459      * properties and fetches the the nodes of all matching properties.
460      * @param keyPart the configuration key iterator
461      * @param node the actual node
462      * @param data here the found nodes are stored
463      */

464     protected void findPropertyNodes(
465         ConfigurationKey.KeyIterator keyPart,
466         Node node,
467         Collection JavaDoc data)
468     {
469         if (!keyPart.hasNext())
470         {
471             data.add(node);
472         } /* if */
473
474         else
475         {
476             String JavaDoc key = keyPart.nextKey(true);
477             Container children = node.getChildren(key);
478             if (keyPart.hasIndex())
479             {
480                 if (keyPart.getIndex() < children.size()
481                     && keyPart.getIndex() >= 0)
482                 {
483                     findPropertyNodes(
484                         (ConfigurationKey.KeyIterator) keyPart.clone(),
485                         (Node) children.get(keyPart.getIndex()),
486                         data);
487                 } /* if */
488             } /* if */
489
490             else
491             {
492                 for (Iterator JavaDoc it = children.iterator(); it.hasNext();)
493                 {
494                     findPropertyNodes(
495                         (ConfigurationKey.KeyIterator) keyPart.clone(),
496                         (Node) it.next(),
497                         data);
498                 } /* for */
499             } /* else */
500         }
501     }
502
503     /**
504      * Checks if the specified node is defined.
505      * @param node the node to be checked
506      * @return a flag if this node is defined
507      */

508     protected boolean nodeDefined(Node node)
509     {
510         DefinedVisitor visitor = new DefinedVisitor();
511         node.visit(visitor, null);
512         return visitor.isDefined();
513     }
514
515     /**
516      * Removes the specified node from this configuration. This method
517      * ensures that parent nodes that become undefined by this operation
518      * are also removed.
519      * @param node the node to be removed
520      */

521     protected void removeNode(Node node)
522     {
523         Node parent = node.getParent();
524         if (parent != null)
525         {
526             parent.remove(node);
527             if (!nodeDefined(parent))
528             {
529                 removeNode(parent);
530             } /* if */
531         } /* if */
532     }
533
534     /**
535      * Returns a reference to the parent node of an add operation.
536      * Nodes for new properties can be added as children of this node.
537      * If the path for the specified key does not exist so far, it is created
538      * now.
539      * @param keyIt the iterator for the key of the new property
540      * @param startNode the node to start the search with
541      * @return the parent node for the add operation
542      */

543     protected Node fetchAddNode(
544         ConfigurationKey.KeyIterator keyIt,
545         Node startNode)
546     {
547         if (!keyIt.hasNext())
548         {
549             throw new IllegalArgumentException JavaDoc("Key must be defined!");
550         } /* if */
551
552         return createAddPath(keyIt, findLastPathNode(keyIt, startNode));
553     }
554
555     /**
556      * Finds the last existing node for an add operation. This method
557      * traverses the configuration tree along the specified key. The last
558      * existing node on this path is returned.
559      * @param keyIt the key iterator
560      * @param node the actual node
561      * @return the last existing node on the given path
562      */

563     protected Node findLastPathNode(
564         ConfigurationKey.KeyIterator keyIt,
565         Node node)
566     {
567         String JavaDoc keyPart = keyIt.nextKey(true);
568
569         if (keyIt.hasNext())
570         {
571             Container c = node.getChildren(keyPart);
572             int idx = (keyIt.hasIndex()) ? keyIt.getIndex() : c.size() - 1;
573             if (idx < 0 || idx >= c.size())
574             {
575                 return node;
576             } /* if */
577             else
578             {
579                 return findLastPathNode(keyIt, (Node) c.get(idx));
580             } /* else */
581         } /* if */
582
583         else
584         {
585             return node;
586         } /* else */
587     }
588
589     /**
590      * Creates the missing nodes for adding a new property. This method
591      * ensures that there are corresponding nodes for all components of the
592      * specified configuration key.
593      * @param keyIt the key iterator
594      * @param root the base node of the path to be created
595      * @return the last node of the path
596      */

597     protected Node createAddPath(ConfigurationKey.KeyIterator keyIt, Node root)
598     {
599         if (keyIt.hasNext())
600         {
601             Node child = new Node(keyIt.currentKey(true));
602             root.addChild(child);
603             keyIt.next();
604             return createAddPath(keyIt, child);
605         } /* if */
606         else
607         {
608             return root;
609         } /* else */
610     }
611
612     /**
613      * Helper method for adding all elements of a collection to a
614      * container.
615      * @param cont the container
616      * @param items the collection to be added
617      */

618     private static void addContainer(Container cont, Collection JavaDoc items)
619     {
620         for (Iterator JavaDoc it = items.iterator(); it.hasNext();)
621         {
622             cont.add(it.next());
623         } /* for */
624     }
625
626     /**
627      * A data class for storing (hierarchical) property information. A property
628      * can have a value and an arbitrary number of child properties.
629      *
630      * @author <a HREF="mailto:oliver.heger@t-online.de">Oliver Heger</a>
631      */

632     public static class Node implements Serializable JavaDoc, Cloneable JavaDoc
633     {
634         /** Stores a reference to this node's parent.*/
635         private Node parent;
636
637         /** Stores the name of this node.*/
638         private String JavaDoc name;
639
640         /** Stores the value of this node.*/
641         private Object JavaDoc value;
642
643         /** Stores the children of this node.*/
644         private Map JavaDoc children;
645
646         /**
647          * Creates a new instance of <code>Node</code>.
648          */

649         public Node()
650         {
651             this(null);
652         }
653
654         /**
655          * Creates a new instance of <code>Node</code> and sets the name.
656          * @param name the node's name
657          */

658         public Node(String JavaDoc name)
659         {
660             setName(name);
661         }
662
663         /**
664          * Returns the name of this node.
665          * @return the node name
666          */

667         public String JavaDoc getName()
668         {
669             return name;
670         }
671
672         /**
673          * Returns the value of this node.
674          * @return the node value (may be <b>null</b>)
675          */

676         public Object JavaDoc getValue()
677         {
678             return value;
679         }
680
681         /**
682          * Returns the parent of this node.
683          * @return this node's parent (can be <b>null</b>)
684          */

685         public Node getParent()
686         {
687             return parent;
688         }
689
690         /**
691          * Sets the name of this node.
692          * @param string the node name
693          */

694         public void setName(String JavaDoc string)
695         {
696             name = string;
697         }
698
699         /**
700          * Sets the value of this node.
701          * @param object the node value
702          */

703         public void setValue(Object JavaDoc object)
704         {
705             value = object;
706         }
707
708         /**
709          * Sets the parent of this node.
710          * @param node the parent node
711          */

712         public void setParent(Node node)
713         {
714             parent = node;
715         }
716
717         /**
718          * Adds the specified child object to this node. Note that there can
719          * be multiple children with the same name.
720          * @param child the child to be added
721          */

722         public void addChild(Node child)
723         {
724             if (children == null)
725             {
726                 children = new SequencedHashMap();
727             } /* if */
728
729             List JavaDoc c = (List JavaDoc) children.get(child.getName());
730             if (c == null)
731             {
732                 c = new ArrayList JavaDoc();
733                 children.put(child.getName(), c);
734             } /* if */
735
736             c.add(child);
737             child.setParent(this);
738         }
739
740         /**
741          * Returns a list with the child nodes of this node.
742          * @return a list with the children (can be empty, but never
743          * <b>null</b>)
744          */

745         public Container getChildren()
746         {
747             Container result = new Container();
748
749             if (children != null)
750             {
751                 for (Iterator JavaDoc it = children.values().iterator(); it.hasNext();)
752                 {
753                     addContainer(result, (Collection JavaDoc) it.next());
754                 } /* for */
755             } /* if */
756
757             return result;
758         }
759
760         /**
761          * Returns a list with this node's children with the given name.
762          * @param name the name of the children
763          * @return a list with all chidren with this name; may be empty, but
764          * never <b>null</b>
765          */

766         public Container getChildren(String JavaDoc name)
767         {
768             if (name == null || children == null)
769             {
770                 return getChildren();
771             } /* if */
772
773             Container cont = new Container();
774             List JavaDoc c = (List JavaDoc) children.get(name);
775             if (c != null)
776             {
777                 addContainer(cont, c);
778             } /* if */
779
780             return cont;
781         }
782
783         /**
784          * Removes the specified child from this node.
785          * @param child the child node to be removed
786          * @return a flag if the child could be found
787          */

788         public boolean remove(Node child)
789         {
790             if (children == null)
791             {
792                 return false;
793             } /* if */
794
795             List JavaDoc c = (List JavaDoc) children.get(child.getName());
796             if (c == null)
797             {
798                 return false;
799             } /* if */
800
801             else
802             {
803                 if (c.remove(child))
804                 {
805                     if (c.isEmpty())
806                     {
807                         children.remove(child.getName());
808                     } /* if */
809                     return true;
810                 } /* if */
811                 else
812                 {
813                     return false;
814                 } /* else */
815             } /* else */
816         }
817
818         /**
819          * Removes all children with the given name.
820          * @param name the name of the children to be removed
821          * @return a flag if children with this name existed
822          */

823         public boolean remove(String JavaDoc name)
824         {
825             if (children == null)
826             {
827                 return false;
828             } /* if */
829
830             return children.remove(name) != null;
831         }
832
833         /**
834          * Removes all children of this node.
835          */

836         public void removeChildren()
837         {
838             children = null;
839         }
840
841         /**
842          * A generic method for traversing this node and all of its children.
843          * This method sends the passed in visitor to this node and all of its
844          * children.
845          * @param visitor the visitor
846          * @param key here a configuration key with the name of the root node
847          * of the iteration can be passed; if this key is not <b>null</b>, the
848          * full pathes to the visited nodes are builded and passed to the
849          * visitor's <code>visit()</code> methods
850          */

851         public void visit(NodeVisitor visitor, ConfigurationKey key)
852         {
853             int length = 0;
854             if (key != null)
855             {
856                 length = key.length();
857                 if (getName() != null)
858                 {
859                     key.append(getName());
860                 } /* if */
861             } /* if */
862
863             visitor.visitBeforeChildren(this, key);
864
865             if (children != null)
866             {
867                 for (Iterator JavaDoc it = children.values().iterator();
868                     it.hasNext() && !visitor.terminate();
869                     )
870                 {
871                     Collection JavaDoc col = (Collection JavaDoc) it.next();
872                     for (Iterator JavaDoc it2 = col.iterator();
873                         it2.hasNext() && !visitor.terminate();
874                         )
875                     {
876                         ((Node) it2.next()).visit(visitor, key);
877                     } /* for */
878                 } /* for */
879             } /* if */
880
881             if (key != null)
882             {
883                 key.setLength(length);
884             } /* if */
885             visitor.visitAfterChildren(this, key);
886         }
887
888         /**
889          * Creates a copy of this object. This is not a deep copy, the children
890          * are not cloned.
891          * @return a copy of this object
892          */

893         protected Object JavaDoc clone()
894         {
895             try
896             {
897                 return super.clone();
898             } /* try */
899             catch (CloneNotSupportedException JavaDoc cex)
900             {
901                 return null; // should not happen
902
} /* catch */
903         }
904     }
905
906     /**
907      * <p>Definition of a visitor class for traversing a node and all of its
908      * children.</p>
909      * <p>This class defines the interface of a visitor for <code>Node</code>
910      * objects and provides a default implementation. The method
911      * <code>visit()</code> of <code>Node</code> implements a generic
912      * iteration algorithm based on the <em>Visitor</em> pattern. By
913      * providing different implementations of visitors it is possible to
914      * collect different data during the iteration process.</p>
915      *
916      * @author <a HREF="mailto:oliver.heger@t-online.de">Oliver Heger</a>
917      */

918     public static class NodeVisitor
919     {
920         /**
921          * Visits the specified node. This method is called during iteration
922          * for each node before its children have been visited.
923          * @param node the actual node
924          * @param key the key of this node (may be <b>null</b>)
925          */

926         public void visitBeforeChildren(Node node, ConfigurationKey key)
927         {
928         }
929
930         /**
931          * Visits the specified node after its children have been processed.
932          * This gives a visitor the opportunity of collecting additional data
933          * after the child nodes have been visited.
934          * @param node the node to be visited
935          * @param key the key of this node (may be <b>null</b>)
936          */

937         public void visitAfterChildren(Node node, ConfigurationKey key)
938         {
939         }
940
941         /**
942          * Returns a flag that indicates if iteration should be stopped. This
943          * method is called after each visited node. It can be useful for
944          * visitors that search a specific node. If this node is found, the
945          * whole process can be stopped. This base implementation always
946          * returns <b>false</b>.
947          * @return a flag if iteration should be stopped
948          */

949         public boolean terminate()
950         {
951             return false;
952         }
953     }
954
955     /**
956      * A specialized visitor that checks if a node is defined.
957      * &quot;Defined&quot; in this terms means that the node or at least one
958      * of its sub nodes is associated with a value.
959      *
960      * @author <a HREF="mailto:oliver.heger@t-online.de">Oliver Heger</a>
961      */

962     static class DefinedVisitor extends NodeVisitor
963     {
964         /** Stores the defined flag.*/
965         private boolean defined;
966
967         /**
968          * Checks if iteration should be stopped. This can be done if the first
969          * defined node is found.
970          * @return a flag if iteration should be stopped
971          */

972         public boolean terminate()
973         {
974             return isDefined();
975         }
976
977         /**
978          * Visits the node. Checks if a value is defined.
979          * @param node the actual node
980          * @param key the key of this node
981          */

982         public void visitBeforeChildren(Node node, ConfigurationKey key)
983         {
984             defined = node.getValue() != null;
985         }
986
987         /**
988          * Returns the defined flag.
989          * @return the defined flag
990          */

991         public boolean isDefined()
992         {
993             return defined;
994         }
995     }
996
997     /**
998      * A specialized visitor that fills a list with keys that are defined in
999      * a node hierarchy.
1000     *
1001     * @author <a HREF="mailto:oliver.heger@t-online.de">Oliver Heger</a>
1002     */

1003    static class DefinedKeysVisitor extends NodeVisitor
1004    {
1005        /** Stores the list to be filled.*/
1006        private Set JavaDoc keyList;
1007
1008        /**
1009         * Default constructor.
1010         */

1011        public DefinedKeysVisitor()
1012        {
1013            keyList = new HashSet JavaDoc();
1014        }
1015
1016        /**
1017         * Returns the list with all defined keys.
1018         * @return the list with the defined keys
1019         */

1020        public Set JavaDoc getKeyList()
1021        {
1022            return keyList;
1023        }
1024
1025        /**
1026         * Visits the specified node. If this node has a value, its key is
1027         * added to the internal list.
1028         * @param node the node to be visited
1029         * @param key the key of this node
1030         */

1031        public void visitBeforeChildren(Node node, ConfigurationKey key)
1032        {
1033            if (node.getValue() != null && key != null)
1034            {
1035                keyList.add(key.toString());
1036            } /* if */
1037        }
1038    }
1039
1040    /**
1041     * A specialized visitor that is able to create a deep copy of a node
1042     * hierarchy.
1043     *
1044     * @author <a HREF="mailto:oliver.heger@t-online.de">Oliver Heger</a>
1045     */

1046    static class CloneVisitor extends NodeVisitor
1047    {
1048        /** A stack with the actual object to be copied.*/
1049        private Stack JavaDoc copyStack;
1050
1051        /** Stores the result of the clone process.*/
1052        private Node result;
1053
1054        /**
1055         * Creates a new instance of <code>CloneVisitor</code>.
1056         */

1057        public CloneVisitor()
1058        {
1059            copyStack = new Stack JavaDoc();
1060        }
1061
1062        /**
1063         * Visits the specified node after its children have been processed.
1064         * @param node the node
1065         * @param key the key of this node
1066         */

1067        public void visitAfterChildren(Node node, ConfigurationKey key)
1068        {
1069            copyStack.pop();
1070            if (copyStack.isEmpty())
1071            {
1072                result = node;
1073            } /* if */
1074        }
1075
1076        /**
1077         * Visits and copies the specified node.
1078         * @param node the node
1079         * @param key the key of this node
1080         */

1081        public void visitBeforeChildren(Node node, ConfigurationKey key)
1082        {
1083            Node copy = (Node) node.clone();
1084            copy.removeChildren();
1085
1086            if (!copyStack.isEmpty())
1087            {
1088                ((Node) copyStack.peek()).addChild(copy);
1089            } /* if */
1090
1091            copyStack.push(copy);
1092        }
1093
1094        /**
1095         * Returns the result of the clone process. This is the root node of
1096         * the cloned node hierarchy.
1097         * @return the cloned root node
1098         */

1099        public Node getClone()
1100        {
1101            return result;
1102        }
1103    }
1104}
1105
Popular Tags