Coverage report

| %line | %branch
--- | --- | ---
org.apache.commons.configuration.HierarchicalConfiguration$CloneVisitor |
100% |
100% |

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

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.