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

Classes in this File | Line Coverage | Branch Coverage | Complexity
HierarchicalConfiguration |
95% | 201/212
100% | 44/44
| 1.858974358974359;1,859
HierarchicalConfiguration$1 |
100% | 3/3
N/A | N/A
| 1.858974358974359;1,859
HierarchicalConfiguration$2 |
100% | 2/2
N/A | N/A
| 1.858974358974359;1,859
HierarchicalConfiguration$3 |
100% | 3/3
N/A | N/A
| 1.858974358974359;1,859
HierarchicalConfiguration$4 |
100% | 3/3
N/A | N/A
| 1.858974358974359;1,859
HierarchicalConfiguration$5 |
100% | 3/3
N/A | N/A
| 1.858974358974359;1,859
HierarchicalConfiguration$BuilderVisitor |
100% | 29/29
100% | 9/9
| 1.858974358974359;1,859
HierarchicalConfiguration$CloneVisitor |
100% | 17/17
100% | 3/3
| 1.858974358974359;1,859
HierarchicalConfiguration$DefinedKeysVisitor |
100% | 16/16
100% | 2/2
| 1.858974358974359;1,859
HierarchicalConfiguration$DefinedVisitor |
100% | 5/5
100% | 1/1
| 1.858974358974359;1,859
HierarchicalConfiguration$Node |
86% | 37/43
82% | 9/11
| 1.858974358974359;1,859
HierarchicalConfiguration$NodeVisitor |
100% | 4/4
N/A | N/A
| 1.858974358974359;1,859

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