##### Coverage Report - org.apache.commons.configuration.HierarchicalConfiguration

Classes in this File | Line Coverage | Branch Coverage | Complexity
HierarchicalConfiguration |
93% | 148/159
100% | 39/39
| 1.9545454545454546;1,955
HierarchicalConfiguration$BuilderVisitor |
100% | 26/26
100% | 9/9
| 1.9545454545454546;1,955
HierarchicalConfiguration$CloneVisitor |
100% | 16/16
100% | 3/3
| 1.9545454545454546;1,955
HierarchicalConfiguration$DefinedKeysVisitor |
100% | 16/16
100% | 2/2
| 1.9545454545454546;1,955
HierarchicalConfiguration$DefinedVisitor |
100% | 5/5
100% | 1/1
| 1.9545454545454546;1,955
HierarchicalConfiguration$Node |
85% | 33/39
82% | 9/11
| 1.9545454545454546;1,955
HierarchicalConfiguration$NodeVisitor |
100% | 4/4
N/A | N/A
| 1.9545454545454546;1,955

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