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

Classes in this File | Line Coverage | Branch Coverage | Complexity
HierarchicalConfiguration |
94% | 185/196
100% | 46/46
| 1.9090909090909092;1,909
HierarchicalConfiguration$1 |
100% | 2/2
N/A | N/A
| 1.9090909090909092;1,909
HierarchicalConfiguration$2 |
100% | 3/3
N/A | N/A
| 1.9090909090909092;1,909
HierarchicalConfiguration$3 |
100% | 3/3
N/A | N/A
| 1.9090909090909092;1,909
HierarchicalConfiguration$4 |
100% | 3/3
N/A | N/A
| 1.9090909090909092;1,909
HierarchicalConfiguration$BuilderVisitor |
100% | 26/26
100% | 9/9
| 1.9090909090909092;1,909
HierarchicalConfiguration$CloneVisitor |
100% | 16/16
100% | 3/3
| 1.9090909090909092;1,909
HierarchicalConfiguration$DefinedKeysVisitor |
100% | 16/16
100% | 2/2
| 1.9090909090909092;1,909
HierarchicalConfiguration$DefinedVisitor |
100% | 5/5
100% | 1/1
| 1.9090909090909092;1,909
HierarchicalConfiguration$Node |
78% | 35/45
82% | 9/11
| 1.9090909090909092;1,909
HierarchicalConfiguration$NodeVisitor |
100% | 4/4
N/A | N/A
| 1.9090909090909092;1,909

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.iterators.SingletonIterator;
30 |  |
     import org.apache.commons.collections.set.ListOrderedSet;
31 |  |
     import org.apache.commons.configuration.event.ConfigurationEvent;
32 |  |
     import org.apache.commons.configuration.event.ConfigurationListener;
33 |  |
     import org.apache.commons.configuration.tree.ConfigurationNode;
34 |  |
     import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;
35 |  |
     import org.apache.commons.configuration.tree.DefaultConfigurationNode;
36 |  |
     import org.apache.commons.configuration.tree.DefaultExpressionEngine;
37 |  |
     import org.apache.commons.configuration.tree.ExpressionEngine;
38 |  |
     import org.apache.commons.configuration.tree.NodeAddData;
39 |  |
     import org.apache.commons.lang.StringUtils;
40 |  |
41 |  |
     /**
42 |  |
      * <p>A specialized configuration class that extends its base class by the
43 |  |
      * ability of keeping more structure in the stored properties.</p><p>There
44 |  |
      * are some sources of configuration data that cannot be stored very well in a
45 |  |
      * <code>BaseConfiguration</code> object because then their structure is lost.
46 |  |
      * This is especially true for XML documents. This class can deal with such
47 |  |
      * structured configuration sources by storing the properties in a tree-like
48 |  |
      * organization.</p><p>The internal used storage form allows for a more
49 |  |
      * sophisticated access to single properties. As an example consider the
50 |  |
      * following XML document:</p><p>
51 |  |
      *
52 |  |
      * <pre>
53 |  |
      * &lt;database&gt;
54 |  |
      *   &lt;tables&gt;
55 |  |
      *     &lt;table&gt;
56 |  |
      *       &lt;name&gt;users&lt;/name&gt;
57 |  |
      *       &lt;fields&gt;
58 |  |
      *         &lt;field&gt;
59 |  |
      *           &lt;name&gt;lid&lt;/name&gt;
60 |  |
      *           &lt;type&gt;long&lt;/name&gt;
61 |  |
      *         &lt;/field&gt;
62 |  |
      *         &lt;field&gt;
63 |  |
      *           &lt;name&gt;usrName&lt;/name&gt;
64 |  |
      *           &lt;type&gt;java.lang.String&lt;/type&gt;
65 |  |
      *         &lt;/field&gt;
66 |  |
      *        ...
67 |  |
      *       &lt;/fields&gt;
68 |  |
      *     &lt;/table&gt;
69 |  |
      *     &lt;table&gt;
70 |  |
      *       &lt;name&gt;documents&lt;/name&gt;
71 |  |
      *       &lt;fields&gt;
72 |  |
      *         &lt;field&gt;
73 |  |
      *           &lt;name&gt;docid&lt;/name&gt;
74 |  |
      *           &lt;type&gt;long&lt;/type&gt;
75 |  |
      *         &lt;/field&gt;
76 |  |
      *         ...
77 |  |
      *       &lt;/fields&gt;
78 |  |
      *     &lt;/table&gt;
79 |  |
      *     ...
80 |  |
      *   &lt;/tables&gt;
81 |  |
      * &lt;/database&gt;
82 |  |
      * </pre>
83 |  |
      *
84 |  |
      * </p><p>If this document is parsed and stored in a
85 |  |
      * <code>HierarchicalConfiguration</code> object (which can be done by one of
86 |  |
      * the sub classes), there are enhanced possibilities of accessing properties.
87 |  |
      * The keys for querying information can contain indices that select a certain
88 |  |
      * element if there are multiple hits.</p><p>For instance the key
89 |  |
      * <code>tables.table(0).name</code> can be used to find out the name of the
90 |  |
      * first table. In opposite <code>tables.table.name</code> would return a
91 |  |
      * collection with the names of all available tables. Similarly the key
92 |  |
      * <code>tables.table(1).fields.field.name</code> returns a collection with
93 |  |
      * the names of all fields of the second table. If another index is added after
94 |  |
      * the <code>field</code> element, a single field can be accessed:
95 |  |
      * <code>tables.table(1).fields.field(0).name</code>.</p><p>There is a
96 |  |
      * <code>getMaxIndex()</code> method that returns the maximum allowed index
97 |  |
      * that can be added to a given property key. This method can be used to iterate
98 |  |
      * over all values defined for a certain property.</p>
99 |  |
      * <p>Since the 1.3 release of <em>Commons Configuration</em> hierarchical
100 |  |
      * configurations support an <em>expression engine</em>. This expression engine
101 |  |
      * is responsible for evaluating the passed in configuration keys and map them
102 |  |
      * to the stored properties. The examples above are valid for the default
103 |  |
      * expression engine, which is used when a new <code>HierarchicalConfiguration</code>
104 |  |
      * instance is created. With the <code>setExpressionEngine()</code> method a
105 |  |
      * different expression engine can be set. For instance with
106 |  |
      * <code>{@link org.apache.commons.configuration.tree.xpath.XPathExpressionEngine}</code>
107 |  |
      * there is an expression engine available that supports configuration keys in
108 |  |
      * XPATH syntax.</p>
109 |  |
      * <p>In addition to the events common for all configuration classes hierarchical
110 |  |
      * configurations support some more events that correspond to some specific
111 |  |
      * methods and features:
112 |  |
      * <dl><dt><em>EVENT_ADD_NODES</em></dt><dd>The <code>addNodes()</code> method
113 |  |
      * was called; the event object contains the key, to which the nodes were added,
114 |  |
      * and a collection with the new nodes as value.</dd>
115 |  |
      * <dt><em>EVENT_CLEAR_TREE</em></dt><dd>The <code>clearTree()</code> method was
116 |  |
      * called; the event object stores the key of the removed sub tree.</dd>
117 |  |
      * <dt><em>EVENT_SUBNODE_CHANGED</em></dt><dd>A <code>SubnodeConfiguration</code>
118 |  |
      * that was created from this configuration has been changed. The value property
119 |  |
      * of the event object contains the original event object as it was sent by the
120 |  |
      * subnode configuration.</dd></dl></p>
121 |  |
      * <p><em>Note:</em>Configuration objects of this type can be read concurrently
122 |  |
      * by multiple threads. However if one of these threads modifies the object,
123 |  |
      * synchronization has to be performed manually.</p>
124 |  |
      *
125 |  |
      * @author Oliver Heger
126 |  |
      * @version $Id: HierarchicalConfiguration.java 722238 2008-12-01 21:28:31Z oheger $
127 |  |
      */
128 |  |
     public class HierarchicalConfiguration extends AbstractConfiguration implements Serializable, Cloneable
129 |  |
     {
130 |  |
         /**
131 |  |
          * Constant for the clear tree event.
132 |  |
          * @since 1.3
133 |  |
          */
134 |  |
         public static final int EVENT_CLEAR_TREE = 10;
135 |  |
136 |  |
         /**
137 |  |
          * Constant for the add nodes event.
138 |  |
          * @since 1.3
139 |  |
          */
140 |  |
         public static final int EVENT_ADD_NODES = 11;
141 |  |
142 |  |
         /**
143 |  |
          * Constant for the subnode configuration modified event.
144 |  |
          * @since 1.5
145 |  |
          */
146 |  |
         public static final int EVENT_SUBNODE_CHANGED = 12;
147 |  |
148 |  |
         /**
149 |  |
          * The serial version UID.
150 |  |
          */
151 |  |
         private static final long serialVersionUID = 3373812230395363192L;
152 |  |
153 |  |
         /** Stores the default expression engine to be used for new objects.*/
154 |  |
         private static ExpressionEngine defaultExpressionEngine;
155 |  |
156 |  |
         /** Stores the root node of this configuration. This field is required for
157 |  |
          * backwards compatibility only.
158 |  |
          */
159 |  |
         private Node root;
160 |  |
161 |  |
         /** Stores the root configuration node.*/
162 |  |
         private ConfigurationNode rootNode;
163 |  |
164 |  |
         /** Stores the expression engine for this instance.*/
165 |  |
         private transient ExpressionEngine expressionEngine;
166 |  |
167 |  |
         /**
168 |  |
          * Creates a new instance of <code>HierarchicalConfiguration</code>.
169 |  |
          */
170 |  |
         public HierarchicalConfiguration()
171 | 1545 |
         {
172 | 1545 |
             setRootNode(new Node());
173 | 1545 |
         }
174 |  |
175 |  |
         /**
176 |  |
          * Creates a new instance of <code>HierarchicalConfiguration</code> and
177 |  |
          * copies all data contained in the specified configuration into the new
178 |  |
          * one.
179 |  |
          *
180 |  |
          * @param c the configuration that is to be copied (if <b>null</b>, this
181 |  |
          * constructor will behave like the standard constructor)
182 |  |
          * @since 1.4
183 |  |
          */
184 |  |
         public HierarchicalConfiguration(HierarchicalConfiguration c)
185 |  |
         {
186 | 14 |
             this();
187 | 14 |
             if (c != null)
188 |  |
             {
189 | 13 |
                 CloneVisitor visitor = new CloneVisitor();
190 | 13 |
                 c.getRootNode().visit(visitor);
191 | 13 |
                 setRootNode(visitor.getClone());
192 |  |
             }
193 | 14 |
         }
194 |  |
195 |  |
         /**
196 |  |
          * Returns the root node of this hierarchical configuration. This method
197 |  |
          * exists for backwards compatibility only. New code should use the
198 |  |
          * <code>{@link #getRootNode()}</code> method instead, which operates on
199 |  |
          * the preferred data type <code>ConfigurationNode</code>.
200 |  |
          *
201 |  |
          * @return the root node
202 |  |
          */
203 |  |
         public Node getRoot()
204 |  |
         {
205 | 2044 |
             if (root == null && rootNode != null)
206 |  |
             {
207 |  |
                 // Dynamically create a snapshot of the root node
208 | 5 |
                 return new Node(rootNode);
209 |  |
             }
210 |  |
211 | 2039 |
             return root;
212 |  |
         }
213 |  |
214 |  |
         /**
215 |  |
          * Sets the root node of this hierarchical configuration. This method
216 |  |
          * exists for backwards compatibility only. New code should use the
217 |  |
          * <code>{@link #setRootNode(ConfigurationNode)}</code> method instead,
218 |  |
          * which operates on the preferred data type <code>ConfigurationNode</code>.
219 |  |
          *
220 |  |
          * @param node the root node
221 |  |
          */
222 |  |
         public void setRoot(Node node)
223 |  |
         {
224 | 37 |
             if (node == null)
225 |  |
             {
226 | 1 |
                 throw new IllegalArgumentException("Root node must not be null!");
227 |  |
             }
228 | 36 |
             root = node;
229 | 36 |
             rootNode = null;
230 | 36 |
         }
231 |  |
232 |  |
         /**
233 |  |
          * Returns the root node of this hierarchical configuration.
234 |  |
          *
235 |  |
          * @return the root node
236 |  |
          * @since 1.3
237 |  |
          */
238 |  |
         public ConfigurationNode getRootNode()
239 |  |
         {
240 | 17992 |
             return (rootNode != null) ? rootNode : root;
241 |  |
         }
242 |  |
243 |  |
         /**
244 |  |
          * Sets the root node of this hierarchical configuration.
245 |  |
          *
246 |  |
          * @param rootNode the root node
247 |  |
          * @since 1.3
248 |  |
          */
249 |  |
         public void setRootNode(ConfigurationNode rootNode)
250 |  |
         {
251 | 1932 |
             if (rootNode == null)
252 |  |
             {
253 | 1 |
                 throw new IllegalArgumentException("Root node must not be null!");
254 |  |
             }
255 | 1931 |
             this.rootNode = rootNode;
256 |  |
257 |  |
             // For backward compatibility also set the old root field.
258 | 1931 |
             root = (rootNode instanceof Node) ? (Node) rootNode : null;
259 | 1931 |
         }
260 |  |
261 |  |
         /**
262 |  |
          * Returns the default expression engine.
263 |  |
          *
264 |  |
          * @return the default expression engine
265 |  |
          * @since 1.3
266 |  |
          */
267 |  |
         public static synchronized ExpressionEngine getDefaultExpressionEngine()
268 |  |
         {
269 | 29450 |
             if (defaultExpressionEngine == null)
270 |  |
             {
271 | 25 |
                 defaultExpressionEngine = new DefaultExpressionEngine();
272 |  |
             }
273 | 29450 |
             return defaultExpressionEngine;
274 |  |
         }
275 |  |
276 |  |
         /**
277 |  |
          * Sets the default expression engine. This expression engine will be used
278 |  |
          * if no specific engine was set for an instance. It is shared between all
279 |  |
          * hierarchical configuration instances. So modifying its properties will
280 |  |
          * impact all instances, for which no specific engine is set.
281 |  |
          *
282 |  |
          * @param engine the new default expression engine
283 |  |
          * @since 1.3
284 |  |
          */
285 |  |
         public static synchronized void setDefaultExpressionEngine(ExpressionEngine engine)
286 |  |
         {
287 | 3 |
             if (engine == null)
288 |  |
             {
289 | 1 |
                 throw new IllegalArgumentException(
290 |  |
                         "Default expression engine must not be null!");
291 |  |
             }
292 | 2 |
             defaultExpressionEngine = engine;
293 | 2 |
         }
294 |  |
295 |  |
         /**
296 |  |
          * Returns the expression engine used by this configuration. This method
297 |  |
          * will never return <b>null</b>; if no specific expression engine was set,
298 |  |
          * the default expression engine will be returned.
299 |  |
          *
300 |  |
          * @return the current expression engine
301 |  |
          * @since 1.3
302 |  |
          */
303 |  |
         public ExpressionEngine getExpressionEngine()
304 |  |
         {
305 | 29882 |
             return (expressionEngine != null) ? expressionEngine
306 |  |
                     : getDefaultExpressionEngine();
307 |  |
         }
308 |  |
309 |  |
         /**
310 |  |
          * Sets the expression engine to be used by this configuration. All property
311 |  |
          * keys this configuration has to deal with will be interpreted by this
312 |  |
          * engine.
313 |  |
          *
314 |  |
          * @param expressionEngine the new expression engine; can be <b>null</b>,
315 |  |
          * then the default expression engine will be used
316 |  |
          * @since 1.3
317 |  |
          */
318 |  |
         public void setExpressionEngine(ExpressionEngine expressionEngine)
319 |  |
         {
320 | 547 |
             this.expressionEngine = expressionEngine;
321 | 547 |
         }
322 |  |
323 |  |
         /**
324 |  |
          * Fetches the specified property. This task is delegated to the associated
325 |  |
          * expression engine.
326 |  |
          *
327 |  |
          * @param key the key to be looked up
328 |  |
          * @return the found value
329 |  |
          */
330 |  |
         public Object getProperty(String key)
331 |  |
         {
332 | 4889 |
             List nodes = fetchNodeList(key);
333 |  |
334 | 4889 |
             if (nodes.size() == 0)
335 |  |
             {
336 | 448 |
                 return null;
337 |  |
             }
338 |  |
             else
339 |  |
             {
340 | 4441 |
                 List list = new ArrayList();
341 | 4441 |
                 for (Iterator it = nodes.iterator(); it.hasNext();)
342 |  |
                 {
343 | 6699 |
                     ConfigurationNode node = (ConfigurationNode) it.next();
344 | 6699 |
                     if (node.getValue() != null)
345 |  |
                     {
346 | 6521 |
                         list.add(node.getValue());
347 |  |
                     }
348 |  |
                 }
349 |  |
350 | 4441 |
                 if (list.size() < 1)
351 |  |
                 {
352 | 34 |
                     return null;
353 |  |
                 }
354 |  |
                 else
355 |  |
                 {
356 | 4407 |
                     return (list.size() == 1) ? list.get(0) : list;
357 |  |
                 }
358 |  |
             }
359 |  |
         }
360 |  |
361 |  |
         /**
362 |  |
          * Adds the property with the specified key. This task will be delegated to
363 |  |
          * the associated <code>ExpressionEngine</code>, so the passed in key
364 |  |
          * must match the requirements of this implementation.
365 |  |
          *
366 |  |
          * @param key the key of the new property
367 |  |
          * @param obj the value of the new property
368 |  |
          */
369 |  |
         protected void addPropertyDirect(String key, Object obj)
370 |  |
         {
371 | 6831 |
             NodeAddData data = getExpressionEngine().prepareAdd(getRootNode(), key);
372 | 6830 |
             ConfigurationNode node = processNodeAddData(data);
373 | 6830 |
             node.setValue(obj);
374 | 6830 |
         }
375 |  |
376 |  |
         /**
377 |  |
          * Adds a collection of nodes at the specified position of the configuration
378 |  |
          * tree. This method works similar to <code>addProperty()</code>, but
379 |  |
          * instead of a single property a whole collection of nodes can be added -
380 |  |
          * and thus complete configuration sub trees. E.g. with this method it is
381 |  |
          * possible to add parts of another <code>HierarchicalConfiguration</code>
382 |  |
          * object to this object. (However be aware that a
383 |  |
          * <code>ConfigurationNode</code> object can only belong to a single
384 |  |
          * configuration. So if nodes from one configuration are directly added to
385 |  |
          * another one using this method, the structure of the source configuration
386 |  |
          * will be broken. In this case you should clone the nodes to be added
387 |  |
          * before calling <code>addNodes()</code>.) If the passed in key refers to
388 |  |
          * an existing and unique node, the new nodes are added to this node.
389 |  |
          * Otherwise a new node will be created at the specified position in the
390 |  |
          * hierarchy.
391 |  |
          *
392 |  |
          * @param key the key where the nodes are to be added; can be <b>null </b>,
393 |  |
          * then they are added to the root node
394 |  |
          * @param nodes a collection with the <code>Node</code> objects to be
395 |  |
          * added
396 |  |
          */
397 |  |
         public void addNodes(String key, Collection nodes)
398 |  |
         {
399 | 32 |
             if (nodes == null || nodes.isEmpty())
400 |  |
             {
401 | 4 |
                 return;
402 |  |
             }
403 |  |
404 | 28 |
             fireEvent(EVENT_ADD_NODES, key, nodes, true);
405 |  |
             ConfigurationNode parent;
406 | 28 |
             List target = fetchNodeList(key);
407 | 28 |
             if (target.size() == 1)
408 |  |
             {
409 |  |
                 // existing unique key
410 | 15 |
                 parent = (ConfigurationNode) target.get(0);
411 |  |
             }
412 |  |
             else
413 |  |
             {
414 |  |
                 // otherwise perform an add operation
415 | 13 |
                 parent = processNodeAddData(getExpressionEngine().prepareAdd(
416 |  |
                         getRootNode(), key));
417 |  |
             }
418 |  |
419 | 28 |
             if (parent.isAttribute())
420 |  |
             {
421 | 1 |
                 throw new IllegalArgumentException(
422 |  |
                         "Cannot add nodes to an attribute node!");
423 |  |
             }
424 |  |
425 | 27 |
             for (Iterator it = nodes.iterator(); it.hasNext();)
426 |  |
             {
427 | 59 |
                 ConfigurationNode child = (ConfigurationNode) it.next();
428 | 59 |
                 if (child.isAttribute())
429 |  |
                 {
430 | 3 |
                     parent.addAttribute(child);
431 |  |
                 }
432 |  |
                 else
433 |  |
                 {
434 | 56 |
                     parent.addChild(child);
435 |  |
                 }
436 | 59 |
                 clearReferences(child);
437 |  |
             }
438 | 27 |
             fireEvent(EVENT_ADD_NODES, key, nodes, false);
439 | 27 |
         }
440 |  |
441 |  |
         /**
442 |  |
          * Checks if this configuration is empty. Empty means that there are no keys
443 |  |
          * with any values, though there can be some (empty) nodes.
444 |  |
          *
445 |  |
          * @return a flag if this configuration is empty
446 |  |
          */
447 |  |
         public boolean isEmpty()
448 |  |
         {
449 | 56 |
             return !nodeDefined(getRootNode());
450 |  |
         }
451 |  |
452 |  |
         /**
453 |  |
          * Creates a new <code>Configuration</code> object containing all keys
454 |  |
          * that start with the specified prefix. This implementation will return a
455 |  |
          * <code>HierarchicalConfiguration</code> object so that the structure of
456 |  |
          * the keys will be saved. The nodes selected by the prefix (it is possible
457 |  |
          * that multiple nodes are selected) are mapped to the root node of the
458 |  |
          * returned configuration, i.e. their children and attributes will become
459 |  |
          * children and attributes of the new root node. However a value of the root
460 |  |
          * node is only set if exactly one of the selected nodes contain a value (if
461 |  |
          * multiple nodes have a value, there is simply no way to decide how these
462 |  |
          * values are merged together). Note that the returned
463 |  |
          * <code>Configuration</code> object is not connected to its source
464 |  |
          * configuration: updates on the source configuration are not reflected in
465 |  |
          * the subset and vice versa.
466 |  |
          *
467 |  |
          * @param prefix the prefix of the keys for the subset
468 |  |
          * @return a new configuration object representing the selected subset
469 |  |
          */
470 |  |
         public Configuration subset(String prefix)
471 |  |
         {
472 | 26 |
             Collection nodes = fetchNodeList(prefix);
473 | 26 |
             if (nodes.isEmpty())
474 |  |
             {
475 | 2 |
                 return new HierarchicalConfiguration();
476 |  |
             }
477 |  |
478 | 24 |
             final HierarchicalConfiguration parent = this;
479 | 24 |
             HierarchicalConfiguration result = new HierarchicalConfiguration()
480 |  |
             {
481 |  |
                 // Override interpolate to always interpolate on the parent
482 | 24 |
                 protected Object interpolate(Object value)
483 |  |
                 {
484 | 14 |
                     return parent.interpolate(value);
485 |  |
                 }
486 |  |
             };
487 | 24 |
             CloneVisitor visitor = new CloneVisitor();
488 |  |
489 |  |
             // Initialize the new root node
490 | 24 |
             Object value = null;
491 | 24 |
             int valueCount = 0;
492 | 24 |
             for (Iterator it = nodes.iterator(); it.hasNext();)
493 |  |
             {
494 | 47 |
                 ConfigurationNode nd = (ConfigurationNode) it.next();
495 | 47 |
                 if (nd.getValue() != null)
496 |  |
                 {
497 | 14 |
                     value = nd.getValue();
498 | 14 |
                     valueCount++;
499 |  |
                 }
500 | 47 |
                 nd.visit(visitor);
501 |  |
502 | 47 |
                 for (Iterator it2 = visitor.getClone().getChildren().iterator(); it2
503 | 148 |
                         .hasNext();)
504 |  |
                 {
505 | 101 |
                     result.getRootNode().addChild((ConfigurationNode) it2.next());
506 |  |
                 }
507 | 47 |
                 for (Iterator it2 = visitor.getClone().getAttributes().iterator(); it2
508 | 48 |
                         .hasNext();)
509 |  |
                 {
510 | 1 |
                     result.getRootNode().addAttribute(
511 |  |
                             (ConfigurationNode) it2.next());
512 |  |
                 }
513 |  |
             }
514 |  |
515 |  |
             // Determine the value of the new root
516 | 24 |
             if (valueCount == 1)
517 |  |
             {
518 | 2 |
                 result.getRootNode().setValue(value);
519 |  |
             }
520 | 24 |
             return (result.isEmpty()) ? new HierarchicalConfiguration() : result;
521 |  |
         }
522 |  |
523 |  |
         /**
524 |  |
          * <p>
525 |  |
          * Returns a hierarchical subnode configuration object that wraps the
526 |  |
          * configuration node specified by the given key. This method provides an
527 |  |
          * easy means of accessing sub trees of a hierarchical configuration. In the
528 |  |
          * returned configuration the sub tree can directly be accessed, it becomes
529 |  |
          * the root node of this configuration. Because of this the passed in key
530 |  |
          * must select exactly one configuration node; otherwise an
531 |  |
          * <code>IllegalArgumentException</code> will be thrown.
532 |  |
          * </p>
533 |  |
          * <p>
534 |  |
          * The difference between this method and the
535 |  |
          * <code>{@link #subset(String)}</code> method is that
536 |  |
          * <code>subset()</code> supports arbitrary subsets of configuration nodes
537 |  |
          * while <code>configurationAt()</code> only returns a single sub tree.
538 |  |
          * Please refer to the documentation of the
539 |  |
          * <code>SubnodeConfiguration</code> class to obtain further information
540 |  |
          * about subnode configurations and when they should be used.
541 |  |
          * </p>
542 |  |
          * <p>
543 |  |
          * With the <code>supportUpdate</code> flag the behavior of the returned
544 |  |
          * <code>SubnodeConfiguration</code> regarding updates of its parent
545 |  |
          * configuration can be determined. A subnode configuration operates on the
546 |  |
          * same nodes as its parent, so changes at one configuration are normally
547 |  |
          * directly visible for the other configuration. There are however changes
548 |  |
          * of the parent configuration, which are not recognized by the subnode
549 |  |
          * configuration per default. An example for this is a reload operation (for
550 |  |
          * file-based configurations): Here the complete node set of the parent
551 |  |
          * configuration is replaced, but the subnode configuration still references
552 |  |
          * the old nodes. If such changes should be detected by the subnode
553 |  |
          * configuration, the <code>supportUpdates</code> flag must be set to
554 |  |
          * <b>true</b>. This causes the subnode configuration to reevaluate the key
555 |  |
          * used for its creation each time it is accessed. This guarantees that the
556 |  |
          * subnode configuration always stays in sync with its key, even if the
557 |  |
          * parent configuration's data significantly changes. If such a change
558 |  |
          * makes the key invalid - because it now no longer points to exactly one
559 |  |
          * node -, the subnode configuration is not reconstructed, but keeps its
560 |  |
          * old data. It is then quasi detached from its parent.
561 |  |
          * </p>
562 |  |
          *
563 |  |
          * @param key the key that selects the sub tree
564 |  |
          * @param supportUpdates a flag whether the returned subnode configuration
565 |  |
          * should be able to handle updates of its parent
566 |  |
          * @return a hierarchical configuration that contains this sub tree
567 |  |
          * @see SubnodeConfiguration
568 |  |
          * @since 1.5
569 |  |
          */
570 |  |
         public SubnodeConfiguration configurationAt(String key,
571 |  |
                 boolean supportUpdates)
572 |  |
         {
573 | 229 |
             List nodes = fetchNodeList(key);
574 | 229 |
             if (nodes.size() != 1)
575 |  |
             {
576 | 25 |
                 throw new IllegalArgumentException(
577 |  |
                         "Passed in key must select exactly one node: " + key);
578 |  |
             }
579 | 204 |
             return supportUpdates ? createSubnodeConfiguration(
580 |  |
                     (ConfigurationNode) nodes.get(0), key)
581 |  |
                     : createSubnodeConfiguration((ConfigurationNode) nodes.get(0));
582 |  |
         }
583 |  |
584 |  |
         /**
585 |  |
          * Returns a hierarchical subnode configuration for the node specified by
586 |  |
          * the given key. This is a short form for <code>configurationAt(key,
587 |  |
          * <b>false</b>)</code>.
588 |  |
          *
589 |  |
          * @param key the key that selects the sub tree
590 |  |
          * @return a hierarchical configuration that contains this sub tree
591 |  |
          * @see SubnodeConfiguration
592 |  |
          * @since 1.3
593 |  |
          */
594 |  |
         public SubnodeConfiguration configurationAt(String key)
595 |  |
         {
596 | 219 |
             return configurationAt(key, false);
597 |  |
         }
598 |  |
599 |  |
         /**
600 |  |
          * Returns a list of sub configurations for all configuration nodes selected
601 |  |
          * by the given key. This method will evaluate the passed in key (using the
602 |  |
          * current <code>ExpressionEngine</code>) and then create a subnode
603 |  |
          * configuration for each returned node (like
604 |  |
          * <code>{@link #configurationAt(String)}</code>}). This is especially
605 |  |
          * useful when dealing with list-like structures. As an example consider the
606 |  |
          * configuration that contains data about database tables and their fields.
607 |  |
          * If you need access to all fields of a certain table, you can simply do
608 |  |
          *
609 |  |
          * <pre>
610 |  |
          * List fields = config.configurationsAt("tables.table(0).fields.field");
611 |  |
          * for(Iterator it = fields.iterator(); it.hasNext();)
612 |  |
          * {
613 |  |
          *     HierarchicalConfiguration sub = (HierarchicalConfiguration) it.next();
614 |  |
          *     // now the children and attributes of the field node can be
615 |  |
          *     // directly accessed
616 |  |
          *     String fieldName = sub.getString("name");
617 |  |
          *     String fieldType = sub.getString("type");
618 |  |
          *     ...
619 |  |
          * </pre>
620 |  |
          *
621 |  |
          * @param key the key for selecting the desired nodes
622 |  |
          * @return a list with hierarchical configuration objects; each
623 |  |
          * configuration represents one of the nodes selected by the passed in key
624 |  |
          * @since 1.3
625 |  |
          */
626 |  |
         public List configurationsAt(String key)
627 |  |
         {
628 | 57 |
             List nodes = fetchNodeList(key);
629 | 57 |
             List configs = new ArrayList(nodes.size());
630 | 57 |
             for (Iterator it = nodes.iterator(); it.hasNext();)
631 |  |
             {
632 | 10 |
                 configs.add(createSubnodeConfiguration((ConfigurationNode) it.next()));
633 |  |
             }
634 | 57 |
             return configs;
635 |  |
         }
636 |  |
637 |  |
         /**
638 |  |
          * Creates a subnode configuration for the specified node. This method is
639 |  |
          * called by <code>configurationAt()</code> and
640 |  |
          * <code>configurationsAt()</code>.
641 |  |
          *
642 |  |
          * @param node the node, for which a subnode configuration is to be created
643 |  |
          * @return the configuration for the given node
644 |  |
          * @since 1.3
645 |  |
          */
646 |  |
         protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node)
647 |  |
         {
648 | 197 |
             SubnodeConfiguration result = new SubnodeConfiguration(this, node);
649 | 197 |
             registerSubnodeConfiguration(result);
650 | 197 |
             return result;
651 |  |
         }
652 |  |
653 |  |
         /**
654 |  |
          * Creates a new subnode configuration for the specified node and sets its
655 |  |
          * construction key. A subnode configuration created this way will be aware
656 |  |
          * of structural changes of its parent.
657 |  |
          *
658 |  |
          * @param node the node, for which a subnode configuration is to be created
659 |  |
          * @param subnodeKey the key used to construct the configuration
660 |  |
          * @return the configuration for the given node
661 |  |
          * @since 1.5
662 |  |
          */
663 |  |
         protected SubnodeConfiguration createSubnodeConfiguration(
664 |  |
                 ConfigurationNode node, String subnodeKey)
665 |  |
         {
666 | 6 |
             SubnodeConfiguration result = createSubnodeConfiguration(node);
667 | 6 |
             result.setSubnodeKey(subnodeKey);
668 | 6 |
             return result;
669 |  |
         }
670 |  |
671 |  |
         /**
672 |  |
          * This method is always called when a subnode configuration created from
673 |  |
          * this configuration has been modified. This implementation transforms the
674 |  |
          * received event into an event of type <code>EVENT_SUBNODE_CHANGED</code>
675 |  |
          * and notifies the registered listeners.
676 |  |
          *
677 |  |
          * @param event the event describing the change
678 |  |
          * @since 1.5
679 |  |
          */
680 |  |
         protected void subnodeConfigurationChanged(ConfigurationEvent event)
681 |  |
         {
682 | 8 |
             fireEvent(EVENT_SUBNODE_CHANGED, null, event, event.isBeforeUpdate());
683 | 8 |
         }
684 |  |
685 |  |
         /**
686 |  |
          * Registers this instance at the given subnode configuration. This
687 |  |
          * implementation will register a change listener, so that modifications of
688 |  |
          * the subnode configuration can be tracked.
689 |  |
          *
690 |  |
          * @param config the subnode configuration
691 |  |
          * @since 1.5
692 |  |
          */
693 |  |
         void registerSubnodeConfiguration(SubnodeConfiguration config)
694 |  |
         {
695 | 327 |
             config.addConfigurationListener(new ConfigurationListener()
696 |  |
             {
697 | 327 |
                 public void configurationChanged(ConfigurationEvent event)
698 |  |
                 {
699 | 8 |
                     subnodeConfigurationChanged(event);
700 | 8 |
                 }
701 |  |
             });
702 | 327 |
         }
703 |  |
704 |  |
         /**
705 |  |
          * Checks if the specified key is contained in this configuration. Note that
706 |  |
          * for this configuration the term &quot;contained&quot; means that the key
707 |  |
          * has an associated value. If there is a node for this key that has no
708 |  |
          * value but children (either defined or undefined), this method will still
709 |  |
          * return <b>false </b>.
710 |  |
          *
711 |  |
          * @param key the key to be chekced
712 |  |
          * @return a flag if this key is contained in this configuration
713 |  |
          */
714 |  |
         public boolean containsKey(String key)
715 |  |
         {
716 | 1281 |
             return getProperty(key) != null;
717 |  |
         }
718 |  |
719 |  |
         /**
720 |  |
          * Sets the value of the specified property.
721 |  |
          *
722 |  |
          * @param key the key of the property to set
723 |  |
          * @param value the new value of this property
724 |  |
          */
725 |  |
         public void setProperty(String key, Object value)
726 |  |
         {
727 | 740 |
             fireEvent(EVENT_SET_PROPERTY, key, value, true);
728 |  |
729 |  |
             // Update the existing nodes for this property
730 | 740 |
             Iterator itNodes = fetchNodeList(key).iterator();
731 |  |
             Iterator itValues;
732 | 740 |
             if (!isDelimiterParsingDisabled())
733 |  |
             {
734 | 738 |
                 itValues = PropertyConverter.toIterator(value, getListDelimiter());
735 |  |
             }
736 |  |
             else
737 |  |
             {
738 | 2 |
                 itValues = new SingletonIterator(value);
739 |  |
             }
740 |  |
741 | 839 |
             while (itNodes.hasNext() && itValues.hasNext())
742 |  |
             {
743 | 99 |
                 ((ConfigurationNode) itNodes.next()).setValue(itValues.next());
744 |  |
             }
745 |  |
746 |  |
             // Add additional nodes if necessary
747 | 1411 |
             while (itValues.hasNext())
748 |  |
             {
749 | 671 |
                 addPropertyDirect(key, itValues.next());
750 |  |
             }
751 |  |
752 |  |
             // Remove remaining nodes
753 | 746 |
             while (itNodes.hasNext())
754 |  |
             {
755 | 6 |
                 clearNode((ConfigurationNode) itNodes.next());
756 |  |
             }
757 |  |
758 | 740 |
             fireEvent(EVENT_SET_PROPERTY, key, value, false);
759 | 740 |
         }
760 |  |
761 |  |
         /**
762 |  |
          * Removes all values of the property with the given name and of keys that
763 |  |
          * start with this name. So if there is a property with the key
764 |  |
          * &quot;foo&quot; and a property with the key &quot;foo.bar&quot;, a call
765 |  |
          * of <code>clearTree("foo")</code> would remove both properties.
766 |  |
          *
767 |  |
          * @param key the key of the property to be removed
768 |  |
          */
769 |  |
         public void clearTree(String key)
770 |  |
         {
771 | 9 |
             fireEvent(EVENT_CLEAR_TREE, key, null, true);
772 | 9 |
             List nodes = fetchNodeList(key);
773 |  |
774 | 9 |
             for (Iterator it = nodes.iterator(); it.hasNext();)
775 |  |
             {
776 | 9 |
                 removeNode((ConfigurationNode) it.next());
777 |  |
             }
778 | 9 |
             fireEvent(EVENT_CLEAR_TREE, key, nodes, false);
779 | 9 |
         }
780 |  |
781 |  |
         /**
782 |  |
          * Removes the property with the given key. Properties with names that start
783 |  |
          * with the given key (i.e. properties below the specified key in the
784 |  |
          * hierarchy) won't be affected.
785 |  |
          *
786 |  |
          * @param key the key of the property to be removed
787 |  |
          */
788 |  |
         public void clearProperty(String key)
789 |  |
         {
790 | 4365 |
             fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
791 | 4365 |
             List nodes = fetchNodeList(key);
792 |  |
793 | 4365 |
             for (Iterator it = nodes.iterator(); it.hasNext();)
794 |  |
             {
795 | 7540 |
                 clearNode((ConfigurationNode) it.next());
796 |  |
             }
797 |  |
798 | 4365 |
             fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
799 | 4365 |
         }
800 |  |
801 |  |
         /**
802 |  |
          * Returns an iterator with all keys defined in this configuration.
803 |  |
          * Note that the keys returned by this method will not contain any
804 |  |
          * indices. This means that some structure will be lost.</p>
805 |  |
          *
806 |  |
          * @return an iterator with the defined keys in this configuration
807 |  |
          */
808 |  |
         public Iterator getKeys()
809 |  |
         {
810 | 201 |
             DefinedKeysVisitor visitor = new DefinedKeysVisitor();
811 | 201 |
             getRootNode().visit(visitor);
812 |  |
813 | 201 |
             return visitor.getKeyList().iterator();
814 |  |
         }
815 |  |
816 |  |
         /**
817 |  |
          * Returns an iterator with all keys defined in this configuration that
818 |  |
          * start with the given prefix. The returned keys will not contain any
819 |  |
          * indices.
820 |  |
          *
821 |  |
          * @param prefix the prefix of the keys to start with
822 |  |
          * @return an iterator with the found keys
823 |  |
          */
824 |  |
         public Iterator getKeys(String prefix)
825 |  |
         {
826 | 14 |
             DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
827 | 14 |
             if (containsKey(prefix))
828 |  |
             {
829 |  |
                 // explicitly add the prefix
830 | 2 |
                 visitor.getKeyList().add(prefix);
831 |  |
             }
832 |  |
833 | 14 |
             List nodes = fetchNodeList(prefix);
834 |  |
835 | 14 |
             for (Iterator itNodes = nodes.iterator(); itNodes.hasNext();)
836 |  |
             {
837 | 14 |
                 ConfigurationNode node = (ConfigurationNode) itNodes.next();
838 | 14 |
                 for (Iterator it = node.getChildren().iterator(); it.hasNext();)
839 |  |
                 {
840 | 29 |
                     ((ConfigurationNode) it.next()).visit(visitor);
841 |  |
                 }
842 | 14 |
                 for (Iterator it = node.getAttributes().iterator(); it.hasNext();)
843 |  |
                 {
844 | 2 |
                     ((ConfigurationNode) it.next()).visit(visitor);
845 |  |
                 }
846 |  |
             }
847 |  |
848 | 14 |
             return visitor.getKeyList().iterator();
849 |  |
         }
850 |  |
851 |  |
         /**
852 |  |
          * Returns the maximum defined index for the given key. This is useful if
853 |  |
          * there are multiple values for this key. They can then be addressed
854 |  |
          * separately by specifying indices from 0 to the return value of this
855 |  |
          * method.
856 |  |
          *
857 |  |
          * @param key the key to be checked
858 |  |
          * @return the maximum defined index for this key
859 |  |
          */
860 |  |
         public int getMaxIndex(String key)
861 |  |
         {
862 | 86 |
             return fetchNodeList(key).size() - 1;
863 |  |
         }
864 |  |
865 |  |
         /**
866 |  |
          * Creates a copy of this object. This new configuration object will contain
867 |  |
          * copies of all nodes in the same structure. Registered event listeners
868 |  |
          * won't be cloned; so they are not registered at the returned copy.
869 |  |
          *
870 |  |
          * @return the copy
871 |  |
          * @since 1.2
872 |  |
          */
873 |  |
         public Object clone()
874 |  |
         {
875 |  |
             try
876 |  |
             {
877 | 11 |
                 HierarchicalConfiguration copy = (HierarchicalConfiguration) super
878 |  |
                         .clone();
879 |  |
880 |  |
                 // clone the nodes, too
881 | 11 |
                 CloneVisitor v = new CloneVisitor();
882 | 11 |
                 getRootNode().visit(v);
883 | 11 |
                 copy.setRootNode(v.getClone());
884 |  |
885 | 11 |
                 return copy;
886 |  |
             }
887 | 0 |
             catch (CloneNotSupportedException cex)
888 |  |
             {
889 |  |
                 // should not happen
890 | 0 |
                 throw new ConfigurationRuntimeException(cex);
891 |  |
             }
892 |  |
         }
893 |  |
894 |  |
         /**
895 |  |
          * Returns a configuration with the same content as this configuration, but
896 |  |
          * with all variables replaced by their actual values. This implementation
897 |  |
          * is specific for hierarchical configurations. It clones the current
898 |  |
          * configuration and runs a specialized visitor on the clone, which performs
899 |  |
          * interpolation on the single configuration nodes.
900 |  |
          *
901 |  |
          * @return a configuration with all variables interpolated
902 |  |
          * @since 1.5
903 |  |
          */
904 |  |
         public Configuration interpolatedConfiguration()
905 |  |
         {
906 | 1 |
             HierarchicalConfiguration c = (HierarchicalConfiguration) clone();
907 | 1 |
             c.getRootNode().visit(new ConfigurationNodeVisitorAdapter()
908 |  |
             {
909 | 1 |
                 public void visitAfterChildren(ConfigurationNode node)
910 |  |
                 {
911 | 39 |
                     node.setValue(interpolate(node.getValue()));
912 | 39 |
                 }
913 |  |
             });
914 | 1 |
             return c;
915 |  |
         }
916 |  |
917 |  |
         /**
918 |  |
          * Helper method for fetching a list of all nodes that are addressed by the
919 |  |
          * specified key.
920 |  |
          *
921 |  |
          * @param key the key
922 |  |
          * @return a list with all affected nodes (never <b>null </b>)
923 |  |
          */
924 |  |
         protected List fetchNodeList(String key)
925 |  |
         {
926 | 10513 |
             return getExpressionEngine().query(getRootNode(), key);
927 |  |
         }
928 |  |
929 |  |
         /**
930 |  |
          * Recursive helper method for fetching a property. This method processes
931 |  |
          * all facets of a configuration key, traverses the tree of properties and
932 |  |
          * fetches the the nodes of all matching properties.
933 |  |
          *
934 |  |
          * @param keyPart the configuration key iterator
935 |  |
          * @param node the actual node
936 |  |
          * @param nodes here the found nodes are stored
937 |  |
          * @deprecated Property keys are now evaluated by the expression engine
938 |  |
          * associated with the configuration; this method will no longer be called.
939 |  |
          * If you want to modify the way properties are looked up, consider
940 |  |
          * implementing you own <code>ExpressionEngine</code> implementation.
941 |  |
          */
942 |  |
         protected void findPropertyNodes(ConfigurationKey.KeyIterator keyPart,
943 |  |
                 Node node, Collection nodes)
944 |  |
         {
945 | 0 |
         }
946 |  |
947 |  |
         /**
948 |  |
          * Checks if the specified node is defined.
949 |  |
          *
950 |  |
          * @param node the node to be checked
951 |  |
          * @return a flag if this node is defined
952 |  |
          * @deprecated Use the method <code>{@link #nodeDefined(ConfigurationNode)}</code>
953 |  |
          * instead.
954 |  |
          */
955 |  |
         protected boolean nodeDefined(Node node)
956 |  |
         {
957 | 0 |
             return nodeDefined((ConfigurationNode) node);
958 |  |
         }
959 |  |
960 |  |
         /**
961 |  |
          * Checks if the specified node is defined.
962 |  |
          *
963 |  |
          * @param node the node to be checked
964 |  |
          * @return a flag if this node is defined
965 |  |
          */
966 |  |
         protected boolean nodeDefined(ConfigurationNode node)
967 |  |
         {
968 | 16949 |
             DefinedVisitor visitor = new DefinedVisitor();
969 | 16949 |
             node.visit(visitor);
970 | 16949 |
             return visitor.isDefined();
971 |  |
         }
972 |  |
973 |  |
         /**
974 |  |
          * Removes the specified node from this configuration. This method ensures
975 |  |
          * that parent nodes that become undefined by this operation are also
976 |  |
          * removed.
977 |  |
          *
978 |  |
          * @param node the node to be removed
979 |  |
          * @deprecated Use the method <code>{@link #removeNode(ConfigurationNode)}</code>
980 |  |
          * instead.
981 |  |
          */
982 |  |
         protected void removeNode(Node node)
983 |  |
         {
984 | 0 |
             removeNode((ConfigurationNode) node);
985 | 0 |
         }
986 |  |
987 |  |
         /**
988 |  |
          * Removes the specified node from this configuration. This method ensures
989 |  |
          * that parent nodes that become undefined by this operation are also
990 |  |
          * removed.
991 |  |
          *
992 |  |
          * @param node the node to be removed
993 |  |
          */
994 |  |
         protected void removeNode(ConfigurationNode node)
995 |  |
         {
996 | 9486 |
             ConfigurationNode parent = node.getParentNode();
997 | 9486 |
             if (parent != null)
998 |  |
             {
999 | 9347 |
                 parent.removeChild(node);
1000 | 9347 |
                 if (!nodeDefined(parent))
1001 |  |
                 {
1002 | 3729 |
                     removeNode(parent);
1003 |  |
                 }
1004 |  |
             }
1005 | 9486 |
         }
1006 |  |
1007 |  |
         /**
1008 |  |
          * Clears the value of the specified node. If the node becomes undefined by
1009 |  |
          * this operation, it is removed from the hierarchy.
1010 |  |
          *
1011 |  |
          * @param node the node to be cleared
1012 |  |
          * @deprecated Use the method <code>{@link #clearNode(ConfigurationNode)}</code>
1013 |  |
          * instead
1014 |  |
          */
1015 |  |
         protected void clearNode(Node node)
1016 |  |
         {
1017 | 0 |
             clearNode((ConfigurationNode) node);
1018 | 0 |
         }
1019 |  |
1020 |  |
         /**
1021 |  |
          * Clears the value of the specified node. If the node becomes undefined by
1022 |  |
          * this operation, it is removed from the hierarchy.
1023 |  |
          *
1024 |  |
          * @param node the node to be cleared
1025 |  |
          */
1026 |  |
         protected void clearNode(ConfigurationNode node)
1027 |  |
         {
1028 | 7546 |
             node.setValue(null);
1029 | 7546 |
             if (!nodeDefined(node))
1030 |  |
             {
1031 | 5748 |
                 removeNode(node);
1032 |  |
             }
1033 | 7546 |
         }
1034 |  |
1035 |  |
         /**
1036 |  |
          * Returns a reference to the parent node of an add operation. Nodes for new
1037 |  |
          * properties can be added as children of this node. If the path for the
1038 |  |
          * specified key does not exist so far, it is created now.
1039 |  |
          *
1040 |  |
          * @param keyIt the iterator for the key of the new property
1041 |  |
          * @param startNode the node to start the search with
1042 |  |
          * @return the parent node for the add operation
1043 |  |
          * @deprecated Adding new properties is now to a major part delegated to the
1044 |  |
          * <code>ExpressionEngine</code> associated with this configuration instance.
1045 |  |
          * This method will no longer be called. Developers who want to modify the
1046 |  |
          * process of adding new properties should consider implementing their own
1047 |  |
          * expression engine.
1048 |  |
          */
1049 |  |
         protected Node fetchAddNode(ConfigurationKey.KeyIterator keyIt, Node startNode)
1050 |  |
         {
1051 | 0 |
             return null;
1052 |  |
         }
1053 |  |
1054 |  |
         /**
1055 |  |
          * Finds the last existing node for an add operation. This method traverses
1056 |  |
          * the configuration tree along the specified key. The last existing node on
1057 |  |
          * this path is returned.
1058 |  |
          *
1059 |  |
          * @param keyIt the key iterator
1060 |  |
          * @param node the actual node
1061 |  |
          * @return the last existing node on the given path
1062 |  |
          * @deprecated Adding new properties is now to a major part delegated to the
1063 |  |
          * <code>ExpressionEngine</code> associated with this configuration instance.
1064 |  |
          * This method will no longer be called. Developers who want to modify the
1065 |  |
          * process of adding new properties should consider implementing their own
1066 |  |
          * expression engine.
1067 |  |
          */
1068 |  |
         protected Node findLastPathNode(ConfigurationKey.KeyIterator keyIt, Node node)
1069 |  |
         {
1070 | 0 |
             return null;
1071 |  |
         }
1072 |  |
1073 |  |
         /**
1074 |  |
          * Creates the missing nodes for adding a new property. This method ensures
1075 |  |
          * that there are corresponding nodes for all components of the specified
1076 |  |
          * configuration key.
1077 |  |
          *
1078 |  |
          * @param keyIt the key iterator
1079 |  |
          * @param root the base node of the path to be created
1080 |  |
          * @return the last node of the path
1081 |  |
          * @deprecated Adding new properties is now to a major part delegated to the
1082 |  |
          * <code>ExpressionEngine</code> associated with this configuration instance.
1083 |  |
          * This method will no longer be called. Developers who want to modify the
1084 |  |
          * process of adding new properties should consider implementing their own
1085 |  |
          * expression engine.
1086 |  |
          */
1087 |  |
         protected Node createAddPath(ConfigurationKey.KeyIterator keyIt, Node root)
1088 |  |
         {
1089 | 0 |
             return null;
1090 |  |
         }
1091 |  |
1092 |  |
         /**
1093 |  |
          * Creates a new <code>Node</code> object with the specified name. This
1094 |  |
          * method can be overloaded in derived classes if a specific node type is
1095 |  |
          * needed. This base implementation always returns a new object of the
1096 |  |
          * <code>Node</code> class.
1097 |  |
          *
1098 |  |
          * @param name the name of the new node
1099 |  |
          * @return the new node
1100 |  |
          */
1101 |  |
         protected Node createNode(String name)
1102 |  |
         {
1103 | 5450 |
             return new Node(name);
1104 |  |
         }
1105 |  |
1106 |  |
         /**
1107 |  |
          * Helper method for processing a node add data object obtained from the
1108 |  |
          * expression engine. This method will create all new nodes.
1109 |  |
          *
1110 |  |
          * @param data the data object
1111 |  |
          * @return the new node
1112 |  |
          * @since 1.3
1113 |  |
          */
1114 |  |
         private ConfigurationNode processNodeAddData(NodeAddData data)
1115 |  |
         {
1116 | 6843 |
             ConfigurationNode node = data.getParent();
1117 |  |
1118 |  |
             // Create missing nodes on the path
1119 | 6843 |
             for (Iterator it = data.getPathNodes().iterator(); it.hasNext();)
1120 |  |
             {
1121 | 1626 |
                 ConfigurationNode child = createNode((String) it.next());
1122 | 1626 |
                 node.addChild(child);
1123 | 1626 |
                 node = child;
1124 |  |
             }
1125 |  |
1126 |  |
             // Add new target node
1127 | 6843 |
             ConfigurationNode child = createNode(data.getNewNodeName());
1128 | 6843 |
             if (data.isAttribute())
1129 |  |
             {
1130 | 125 |
                 node.addAttribute(child);
1131 |  |
             }
1132 |  |
             else
1133 |  |
             {
1134 | 6718 |
                 node.addChild(child);
1135 |  |
             }
1136 | 6843 |
             return child;
1137 |  |
         }
1138 |  |
1139 |  |
         /**
1140 |  |
          * Clears all reference fields in a node structure. A configuration node can
1141 |  |
          * store a so-called &quot;reference&quot;. The meaning of this data is
1142 |  |
          * determined by a concrete sub class. Typically such references are
1143 |  |
          * specific for a configuration instance. If this instance is cloned or
1144 |  |
          * copied, they must be cleared. This can be done using this method.
1145 |  |
          *
1146 |  |
          * @param node the root node of the node hierarchy, in which the references
1147 |  |
          * are to be cleared
1148 |  |
          * @since 1.4
1149 |  |
          */
1150 |  |
         protected static void clearReferences(ConfigurationNode node)
1151 |  |
         {
1152 | 70 |
             node.visit(new ConfigurationNodeVisitorAdapter()
1153 |  |
             {
1154 | 70 |
                 public void visitBeforeChildren(ConfigurationNode node)
1155 |  |
                 {
1156 | 980 |
                     node.setReference(null);
1157 | 980 |
                 }
1158 |  |
             });
1159 | 70 |
         }
1160 |  |
1161 |  |
         /**
1162 |  |
          * A data class for storing (hierarchical) property information. A property
1163 |  |
          * can have a value and an arbitrary number of child properties. From
1164 |  |
          * version 1.3 on this class is only a thin wrapper over the
1165 |  |
          * <code>{@link org.apache.commons.configuration.tree.DefaultConfigurationNode DefaultconfigurationNode}</code>
1166 |  |
          * class that exists mainly for the purpose of backwards compatibility.
1167 |  |
          */
1168 |  |
         public static class Node extends DefaultConfigurationNode implements Serializable
1169 |  |
         {
1170 |  |
             /**
1171 |  |
              * The serial version UID.
1172 |  |
              */
1173 |  |
             private static final long serialVersionUID = -6357500633536941775L;
1174 |  |
1175 |  |
             /**
1176 |  |
              * Creates a new instance of <code>Node</code>.
1177 |  |
              */
1178 |  |
             public Node()
1179 |  |
             {
1180 | 2917 |
                 super();
1181 | 2917 |
             }
1182 |  |
1183 |  |
             /**
1184 |  |
              * Creates a new instance of <code>Node</code> and sets the name.
1185 |  |
              *
1186 |  |
              * @param name the node's name
1187 |  |
              */
1188 |  |
             public Node(String name)
1189 |  |
             {
1190 | 41105 |
                 super(name);
1191 | 41105 |
             }
1192 |  |
1193 |  |
             /**
1194 |  |
              * Creates a new instance of <code>Node</code> and sets the name and the value.
1195 |  |
              *
1196 |  |
              * @param name the node's name
1197 |  |
              * @param value the value
1198 |  |
              */
1199 |  |
             public Node(String name, Object value)
1200 |  |
             {
1201 | 8 |
                 super(name, value);
1202 | 8 |
             }
1203 |  |
1204 |  |
             /**
1205 |  |
              * Creates a new instance of <code>Node</code> based on the given
1206 |  |
              * source node. All properties of the source node, including its
1207 |  |
              * children and attributes, will be copied.
1208 |  |
              *
1209 |  |
              * @param src the node to be copied
1210 |  |
              */
1211 |  |
             public Node(ConfigurationNode src)
1212 |  |
             {
1213 | 5 |
                 this(src.getName(), src.getValue());
1214 | 5 |
                 setReference(src.getReference());
1215 | 5 |
                 for (Iterator it = src.getChildren().iterator(); it.hasNext();)
1216 |  |
                 {
1217 | 10 |
                     ConfigurationNode nd = (ConfigurationNode) it.next();
1218 |  |
                     // Don't change the parent node
1219 | 10 |
                     ConfigurationNode parent = nd.getParentNode();
1220 | 10 |
                     addChild(nd);
1221 | 10 |
                     nd.setParentNode(parent);
1222 |  |
                 }
1223 |  |
1224 | 5 |
                 for (Iterator it = src.getAttributes().iterator(); it.hasNext();)
1225 |  |
                 {
1226 | 0 |
                     ConfigurationNode nd = (ConfigurationNode) it.next();
1227 |  |
                     // Don't change the parent node
1228 | 0 |
                     ConfigurationNode parent = nd.getParentNode();
1229 | 0 |
                     addAttribute(nd);
1230 | 0 |
                     nd.setParentNode(parent);
1231 |  |
                 }
1232 | 5 |
             }
1233 |  |
1234 |  |
             /**
1235 |  |
              * Returns the parent of this node.
1236 |  |
              *
1237 |  |
              * @return this node's parent (can be <b>null</b>)
1238 |  |
              */
1239 |  |
             public Node getParent()
1240 |  |
             {
1241 | 2120 |
                 return (Node) getParentNode();
1242 |  |
             }
1243 |  |
1244 |  |
             /**
1245 |  |
              * Sets the parent of this node.
1246 |  |
              *
1247 |  |
              * @param node the parent node
1248 |  |
              */
1249 |  |
             public void setParent(Node node)
1250 |  |
             {
1251 | 0 |
                 setParentNode(node);
1252 | 0 |
             }
1253 |  |
1254 |  |
             /**
1255 |  |
              * Adds the given node to the children of this node.
1256 |  |
              *
1257 |  |
              * @param node the child to be added
1258 |  |
              */
1259 |  |
             public void addChild(Node node)
1260 |  |
             {
1261 | 25313 |
                 addChild((ConfigurationNode) node);
1262 | 25313 |
             }
1263 |  |
1264 |  |
             /**
1265 |  |
              * Returns a flag whether this node has child elements.
1266 |  |
              *
1267 |  |
              * @return <b>true</b> if there is a child node, <b>false</b> otherwise
1268 |  |
              */
1269 |  |
             public boolean hasChildren()
1270 |  |
             {
1271 | 7743 |
                 return getChildrenCount() > 0 || getAttributeCount() > 0;
1272 |  |
             }
1273 |  |
1274 |  |
             /**
1275 |  |
              * Removes the specified child from this node.
1276 |  |
              *
1277 |  |
              * @param child the child node to be removed
1278 |  |
              * @return a flag if the child could be found
1279 |  |
              */
1280 |  |
             public boolean remove(Node child)
1281 |  |
             {
1282 | 834 |
                 return child.isAttribute() ? removeAttribute(child) : removeChild(child);
1283 |  |
             }
1284 |  |
1285 |  |
             /**
1286 |  |
              * Removes all children with the given name.
1287 |  |
              *
1288 |  |
              * @param name the name of the children to be removed
1289 |  |
              * @return a flag if children with this name existed
1290 |  |
              */
1291 |  |
             public boolean remove(String name)
1292 |  |
             {
1293 | 5 |
                 boolean childrenRemoved = removeChild(name);
1294 | 5 |
                 boolean attrsRemoved = removeAttribute(name);
1295 | 5 |
                 return childrenRemoved || attrsRemoved;
1296 |  |
             }
1297 |  |
1298 |  |
             /**
1299 |  |
              * A generic method for traversing this node and all of its children.
1300 |  |
              * This method sends the passed in visitor to this node and all of its
1301 |  |
              * children.
1302 |  |
              *
1303 |  |
              * @param visitor the visitor
1304 |  |
              * @param key here a configuration key with the name of the root node of
1305 |  |
              * the iteration can be passed; if this key is not <b>null </b>, the
1306 |  |
              * full pathes to the visited nodes are builded and passed to the
1307 |  |
              * visitor's <code>visit()</code> methods
1308 |  |
              */
1309 |  |
             public void visit(NodeVisitor visitor, ConfigurationKey key)
1310 |  |
             {
1311 | 3907 |
                 int length = 0;
1312 | 3907 |
                 if (key != null)
1313 |  |
                 {
1314 | 0 |
                     length = key.length();
1315 | 0 |
                     if (getName() != null)
1316 |  |
                     {
1317 | 0 |
                         key
1318 |  |
                                 .append(StringUtils
1319 |  |
                                         .replace(
1320 |  |
                                                 isAttribute() ? ConfigurationKey
1321 |  |
                                                         .constructAttributeKey(getName())
1322 |  |
                                                         : getName(),
1323 |  |
                                                 String
1324 |  |
                                                         .valueOf(ConfigurationKey.PROPERTY_DELIMITER),
1325 |  |
                                                 ConfigurationKey.ESCAPED_DELIMITER));
1326 |  |
                     }
1327 |  |
                 }
1328 |  |
1329 | 3907 |
                 visitor.visitBeforeChildren(this, key);
1330 |  |
1331 | 3907 |
                 for (Iterator it = getChildren().iterator(); it.hasNext()
1332 | 6749 |
                         && !visitor.terminate();)
1333 |  |
                 {
1334 | 2842 |
                     ((Node) it.next()).visit(visitor, key);
1335 |  |
                 }
1336 | 3907 |
                 for (Iterator it = getAttributes().iterator(); it.hasNext()
1337 | 4908 |
                         && !visitor.terminate();)
1338 |  |
                 {
1339 | 1001 |
                     ((Node) it.next()).visit(visitor, key);
1340 |  |
                 }
1341 |  |
1342 | 3907 |
                 if (key != null)
1343 |  |
                 {
1344 | 0 |
                     key.setLength(length);
1345 |  |
                 }
1346 | 3907 |
                 visitor.visitAfterChildren(this, key);
1347 | 3907 |
             }
1348 |  |
         }
1349 |  |
1350 |  |
         /**
1351 |  |
          * <p>Definition of a visitor class for traversing a node and all of its
1352 |  |
          * children.</p><p>This class defines the interface of a visitor for
1353 |  |
          * <code>Node</code> objects and provides a default implementation. The
1354 |  |
          * method <code>visit()</code> of <code>Node</code> implements a generic
1355 |  |
          * iteration algorithm based on the <em>Visitor</em> pattern. By providing
1356 |  |
          * different implementations of visitors it is possible to collect different
1357 |  |
          * data during the iteration process.</p>
1358 |  |
          *
1359 |  |
          */
1360 | 64 |
         public static class NodeVisitor
1361 |  |
         {
1362 |  |
             /**
1363 |  |
              * Visits the specified node. This method is called during iteration for
1364 |  |
              * each node before its children have been visited.
1365 |  |
              *
1366 |  |
              * @param node the actual node
1367 |  |
              * @param key the key of this node (may be <b>null </b>)
1368 |  |
              */
1369 |  |
             public void visitBeforeChildren(Node node, ConfigurationKey key)
1370 |  |
             {
1371 | 28 |
             }
1372 |  |
1373 |  |
             /**
1374 |  |
              * Visits the specified node after its children have been processed.
1375 |  |
              * This gives a visitor the opportunity of collecting additional data
1376 |  |
              * after the child nodes have been visited.
1377 |  |
              *
1378 |  |
              * @param node the node to be visited
1379 |  |
              * @param key the key of this node (may be <b>null </b>)
1380 |  |
              */
1381 |  |
             public void visitAfterChildren(Node node, ConfigurationKey key)
1382 |  |
             {
1383 | 3867 |
             }
1384 |  |
1385 |  |
             /**
1386 |  |
              * Returns a flag that indicates if iteration should be stopped. This
1387 |  |
              * method is called after each visited node. It can be useful for
1388 |  |
              * visitors that search a specific node. If this node is found, the
1389 |  |
              * whole process can be stopped. This base implementation always returns
1390 |  |
              * <b>false </b>.
1391 |  |
              *
1392 |  |
              * @return a flag if iteration should be stopped
1393 |  |
              */
1394 |  |
             public boolean terminate()
1395 |  |
             {
1396 | 3804 |
                 return false;
1397 |  |
             }
1398 |  |
         }
1399 |  |
1400 |  |
         /**
1401 |  |
          * A specialized visitor that checks if a node is defined.
1402 |  |
          * &quot;Defined&quot; in this terms means that the node or at least one of
1403 |  |
          * its sub nodes is associated with a value.
1404 |  |
          *
1405 |  |
          */
1406 | 16949 |
         static class DefinedVisitor extends ConfigurationNodeVisitorAdapter
1407 |  |
         {
1408 |  |
             /** Stores the defined flag. */
1409 |  |
             private boolean defined;
1410 |  |
1411 |  |
             /**
1412 |  |
              * Checks if iteration should be stopped. This can be done if the first
1413 |  |
              * defined node is found.
1414 |  |
              *
1415 |  |
              * @return a flag if iteration should be stopped
1416 |  |
              */
1417 |  |
             public boolean terminate()
1418 |  |
             {
1419 | 53422 |
                 return isDefined();
1420 |  |
             }
1421 |  |
1422 |  |
             /**
1423 |  |
              * Visits the node. Checks if a value is defined.
1424 |  |
              *
1425 |  |
              * @param node the actual node
1426 |  |
              */
1427 |  |
             public void visitBeforeChildren(ConfigurationNode node)
1428 |  |
             {
1429 | 31535 |
                 defined = node.getValue() != null;
1430 | 31535 |
             }
1431 |  |
1432 |  |
             /**
1433 |  |
              * Returns the defined flag.
1434 |  |
              *
1435 |  |
              * @return the defined flag
1436 |  |
              */
1437 |  |
             public boolean isDefined()
1438 |  |
             {
1439 | 70371 |
                 return defined;
1440 |  |
             }
1441 |  |
         }
1442 |  |
1443 |  |
         /**
1444 |  |
          * A specialized visitor that fills a list with keys that are defined in a
1445 |  |
          * node hierarchy.
1446 |  |
          */
1447 |  |
         class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter
1448 |  |
         {
1449 |  |
             /** Stores the list to be filled. */
1450 |  |
             private Set keyList;
1451 |  |
1452 |  |
             /** A stack with the keys of the already processed nodes. */
1453 |  |
             private Stack parentKeys;
1454 |  |
1455 |  |
             /**
1456 |  |
              * Default constructor.
1457 |  |
              */
1458 |  |
             public DefinedKeysVisitor()
1459 | 215 |
             {
1460 | 215 |
                 keyList = new ListOrderedSet();
1461 | 215 |
                 parentKeys = new Stack();
1462 | 215 |
             }
1463 |  |
1464 |  |
             /**
1465 |  |
              * Creates a new <code>DefinedKeysVisitor</code> instance and sets the
1466 |  |
              * prefix for the keys to fetch.
1467 |  |
              *
1468 |  |
              * @param prefix the prefix
1469 |  |
              */
1470 |  |
             public DefinedKeysVisitor(String prefix)
1471 |  |
             {
1472 | 14 |
                 this();
1473 | 14 |
                 parentKeys.push(prefix);
1474 | 14 |
             }
1475 |  |
1476 |  |
             /**
1477 |  |
              * Returns the list with all defined keys.
1478 |  |
              *
1479 |  |
              * @return the list with the defined keys
1480 |  |
              */
1481 |  |
             public Set getKeyList()
1482 |  |
             {
1483 | 217 |
                 return keyList;
1484 |  |
             }
1485 |  |
1486 |  |
             /**
1487 |  |
              * Visits the node after its children has been processed. Removes this
1488 |  |
              * node's key from the stack.
1489 |  |
              *
1490 |  |
              * @param node the node
1491 |  |
              */
1492 |  |
             public void visitAfterChildren(ConfigurationNode node)
1493 |  |
             {
1494 | 12164 |
                 parentKeys.pop();
1495 | 12164 |
             }
1496 |  |
1497 |  |
             /**
1498 |  |
              * Visits the specified node. If this node has a value, its key is added
1499 |  |
              * to the internal list.
1500 |  |
              *
1501 |  |
              * @param node the node to be visited
1502 |  |
              */
1503 |  |
             public void visitBeforeChildren(ConfigurationNode node)
1504 |  |
             {
1505 | 12164 |
                 String parentKey = parentKeys.isEmpty() ? null
1506 |  |
                         : (String) parentKeys.peek();
1507 | 12164 |
                 String key = getExpressionEngine().nodeKey(node, parentKey);
1508 | 12164 |
                 parentKeys.push(key);
1509 | 12164 |
                 if (node.getValue() != null)
1510 |  |
                 {
1511 | 9582 |
                     keyList.add(key);
1512 |  |
                 }
1513 | 12164 |
             }
1514 |  |
         }
1515 |  |
1516 |  |
         /**
1517 |  |
          * A specialized visitor that is able to create a deep copy of a node
1518 |  |
          * hierarchy.
1519 |  |
          */
1520 |  |
         static class CloneVisitor extends ConfigurationNodeVisitorAdapter
1521 |  |
         {
1522 |  |
             /** A stack with the actual object to be copied. */
1523 |  |
             private Stack copyStack;
1524 |  |
1525 |  |
             /** Stores the result of the clone process. */
1526 |  |
             private ConfigurationNode result;
1527 |  |
1528 |  |
             /**
1529 |  |
              * Creates a new instance of <code>CloneVisitor</code>.
1530 |  |
              */
1531 |  |
             public CloneVisitor()
1532 | 48 |
             {
1533 | 48 |
                 copyStack = new Stack();
1534 | 48 |
             }
1535 |  |
1536 |  |
             /**
1537 |  |
              * Visits the specified node after its children have been processed.
1538 |  |
              *
1539 |  |
              * @param node the node
1540 |  |
              */
1541 |  |
             public void visitAfterChildren(ConfigurationNode node)
1542 |  |
             {
1543 | 826 |
                 ConfigurationNode copy = (ConfigurationNode) copyStack.pop();
1544 | 826 |
                 if (copyStack.isEmpty())
1545 |  |
                 {
1546 | 71 |
                     result = copy;
1547 |  |
                 }
1548 | 826 |
             }
1549 |  |
1550 |  |
             /**
1551 |  |
              * Visits and copies the specified node.
1552 |  |
              *
1553 |  |
              * @param node the node
1554 |  |
              */
1555 |  |
             public void visitBeforeChildren(ConfigurationNode node)
1556 |  |
             {
1557 | 826 |
                 ConfigurationNode copy = (ConfigurationNode) node.clone();
1558 | 826 |
                 copy.setParentNode(null);
1559 |  |
1560 | 826 |
                 if (!copyStack.isEmpty())
1561 |  |
                 {
1562 | 755 |
                     if (node.isAttribute())
1563 |  |
                     {
1564 | 78 |
                         ((ConfigurationNode) copyStack.peek()).addAttribute(copy);
1565 |  |
                     }
1566 |  |
                     else
1567 |  |
                     {
1568 | 677 |
                         ((ConfigurationNode) copyStack.peek()).addChild(copy);
1569 |  |
                     }
1570 |  |
                 }
1571 |  |
1572 | 826 |
                 copyStack.push(copy);
1573 | 826 |
             }
1574 |  |
1575 |  |
             /**
1576 |  |
              * Returns the result of the clone process. This is the root node of the
1577 |  |
              * cloned node hierarchy.
1578 |  |
              *
1579 |  |
              * @return the cloned root node
1580 |  |
              */
1581 |  |
             public ConfigurationNode getClone()
1582 |  |
             {
1583 | 118 |
                 return result;
1584 |  |
             }
1585 |  |
         }
1586 |  |
1587 |  |
         /**
1588 |  |
          * A specialized visitor base class that can be used for storing the tree of
1589 |  |
          * configuration nodes. The basic idea is that each node can be associated
1590 |  |
          * with a reference object. This reference object has a concrete meaning in
1591 |  |
          * a derived class, e.g. an entry in a JNDI context or an XML element. When
1592 |  |
          * the configuration tree is set up, the <code>load()</code> method is
1593 |  |
          * responsible for setting the reference objects. When the configuration
1594 |  |
          * tree is later modified, new nodes do not have a defined reference object.
1595 |  |
          * This visitor class processes all nodes and finds the ones without a
1596 |  |
          * defined reference object. For those nodes the <code>insert()</code>
1597 |  |
          * method is called, which must be defined in concrete sub classes. This
1598 |  |
          * method can perform all steps to integrate the new node into the original
1599 |  |
          * structure.
1600 |  |
          *
1601 |  |
          */
1602 | 62 |
         protected abstract static class BuilderVisitor extends NodeVisitor
1603 |  |
         {
1604 |  |
             /**
1605 |  |
              * Visits the specified node before its children have been traversed.
1606 |  |
              *
1607 |  |
              * @param node the node to visit
1608 |  |
              * @param key the current key
1609 |  |
              */
1610 |  |
             public void visitBeforeChildren(Node node, ConfigurationKey key)
1611 |  |
             {
1612 | 3839 |
                 Collection subNodes = new LinkedList(node.getChildren());
1613 | 3839 |
                 subNodes.addAll(node.getAttributes());
1614 | 3839 |
                 Iterator children = subNodes.iterator();
1615 | 3839 |
                 Node sibling1 = null;
1616 | 3839 |
                 Node nd = null;
1617 |  |
1618 | 5417 |
                 while (children.hasNext())
1619 |  |
                 {
1620 |  |
                     // find the next new node
1621 |  |
                     do
1622 |  |
                     {
1623 | 3328 |
                         sibling1 = nd;
1624 | 3328 |
                         nd = (Node) children.next();
1625 | 3328 |
                     } while (nd.getReference() != null && children.hasNext());
1626 |  |
1627 | 1578 |
                     if (nd.getReference() == null)
1628 |  |
                     {
1629 |  |
                         // find all following new nodes
1630 | 306 |
                         List newNodes = new LinkedList();
1631 | 306 |
                         newNodes.add(nd);
1632 | 732 |
                         while (children.hasNext())
1633 |  |
                         {
1634 | 449 |
                             nd = (Node) children.next();
1635 | 449 |
                             if (nd.getReference() == null)
1636 |  |
                             {
1637 | 426 |
                                 newNodes.add(nd);
1638 |  |
                             }
1639 |  |
                             else
1640 |  |
                             {
1641 |  |
                                 break;
1642 |  |
                             }
1643 |  |
                         }
1644 |  |
1645 |  |
                         // Insert all new nodes
1646 | 306 |
                         Node sibling2 = (nd.getReference() == null) ? null : nd;
1647 | 306 |
                         for (Iterator it = newNodes.iterator(); it.hasNext();)
1648 |  |
                         {
1649 | 732 |
                             Node insertNode = (Node) it.next();
1650 | 732 |
                             if (insertNode.getReference() == null)
1651 |  |
                             {
1652 | 670 |
                                 Object ref = insert(insertNode, node, sibling1, sibling2);
1653 | 670 |
                                 if (ref != null)
1654 |  |
                                 {
1655 | 546 |
                                     insertNode.setReference(ref);
1656 |  |
                                 }
1657 | 670 |
                                 sibling1 = insertNode;
1658 |  |
                             }
1659 |  |
                         }
1660 |  |
                     }
1661 |  |
                 }
1662 | 3839 |
             }
1663 |  |
1664 |  |
             /**
1665 |  |
              * Inserts a new node into the structure constructed by this builder.
1666 |  |
              * This method is called for each node that has been added to the
1667 |  |
              * configuration tree after the configuration has been loaded from its
1668 |  |
              * source. These new nodes have to be inserted into the original
1669 |  |
              * structure. The passed in nodes define the position of the node to be
1670 |  |
              * inserted: its parent and the siblings between to insert. The return
1671 |  |
              * value is interpreted as the new reference of the affected
1672 |  |
              * <code>Node</code> object; if it is not <b>null </b>, it is passed
1673 |  |
              * to the node's <code>setReference()</code> method.
1674 |  |
              *
1675 |  |
              * @param newNode the node to be inserted
1676 |  |
              * @param parent the parent node
1677 |  |
              * @param sibling1 the sibling after which the node is to be inserted;
1678 |  |
              * can be <b>null </b> if the new node is going to be the first child
1679 |  |
              * node
1680 |  |
              * @param sibling2 the sibling before which the node is to be inserted;
1681 |  |
              * can be <b>null </b> if the new node is going to be the last child
1682 |  |
              * node
1683 |  |
              * @return the reference object for the node to be inserted
1684 |  |
              */
1685 |  |
             protected abstract Object insert(Node newNode, Node parent, Node sibling1, Node sibling2);
1686 |  |
         }
1687 |  |
     }