1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 * <database>
55 * <tables>
56 * <table>
57 * <name>users</name>
58 * <fields>
59 * <field>
60 * <name>lid</name>
61 * <type>long</name>
62 * </field>
63 * <field>
64 * <name>usrName</name>
65 * <type>java.lang.String</type>
66 * </field>
67 * ...
68 * </fields>
69 * </table>
70 * <table>
71 * <name>documents</name>
72 * <fields>
73 * <field>
74 * <name>docid</name>
75 * <type>long</type>
76 * </field>
77 * ...
78 * </fields>
79 * </table>
80 * ...
81 * </tables>
82 * </database>
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 {
173 setRootNode(new Node());
174 }
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 this();
188 if (c != null)
189 {
190 CloneVisitor visitor = new CloneVisitor();
191 c.getRootNode().visit(visitor);
192 setRootNode(visitor.getClone());
193 }
194 }
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 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 if (node == null)
220 {
221 throw new IllegalArgumentException("Root node must not be null!");
222 }
223 root = node;
224 rootNode = null;
225 }
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 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 if (rootNode == null)
247 {
248 throw new IllegalArgumentException("Root node must not be null!");
249 }
250 this.rootNode = rootNode;
251
252
253 root = (rootNode instanceof Node) ? (Node) rootNode : new Node(rootNode);
254 }
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 if (defaultExpressionEngine == null)
265 {
266 defaultExpressionEngine = new DefaultExpressionEngine();
267 }
268 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 if (engine == null)
283 {
284 throw new IllegalArgumentException(
285 "Default expression engine must not be null!");
286 }
287 defaultExpressionEngine = engine;
288 }
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 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 this.expressionEngine = expressionEngine;
316 }
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 List nodes = fetchNodeList(key);
328
329 if (nodes.size() == 0)
330 {
331 return null;
332 }
333 else
334 {
335 List list = new ArrayList();
336 for (Iterator it = nodes.iterator(); it.hasNext();)
337 {
338 ConfigurationNode node = (ConfigurationNode) it.next();
339 if (node.getValue() != null)
340 {
341 list.add(node.getValue());
342 }
343 }
344
345 if (list.size() < 1)
346 {
347 return null;
348 }
349 else
350 {
351 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 NodeAddData data = getExpressionEngine().prepareAdd(getRootNode(), key);
367 ConfigurationNode node = processNodeAddData(data);
368 node.setValue(obj);
369 }
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 if (nodes == null || nodes.isEmpty())
395 {
396 return;
397 }
398
399 fireEvent(EVENT_ADD_NODES, key, nodes, true);
400 ConfigurationNode parent;
401 List target = fetchNodeList(key);
402 if (target.size() == 1)
403 {
404
405 parent = (ConfigurationNode) target.get(0);
406 }
407 else
408 {
409
410 parent = processNodeAddData(getExpressionEngine().prepareAdd(
411 getRootNode(), key));
412 }
413
414 if (parent.isAttribute())
415 {
416 throw new IllegalArgumentException(
417 "Cannot add nodes to an attribute node!");
418 }
419
420
421
422 ConfigurationNodeVisitor clearRefVisitor = new ConfigurationNodeVisitorAdapter()
423 {
424 public void visitBeforeChildren(ConfigurationNode node)
425 {
426 node.setReference(null);
427 }
428 };
429
430 for (Iterator it = nodes.iterator(); it.hasNext();)
431 {
432 ConfigurationNode child = (ConfigurationNode) it.next();
433 if (child.isAttribute())
434 {
435 parent.addAttribute(child);
436 }
437 else
438 {
439 parent.addChild(child);
440 }
441 child.visit(clearRefVisitor);
442 }
443 fireEvent(EVENT_ADD_NODES, key, nodes, false);
444 }
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 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 Collection nodes = fetchNodeList(prefix);
478 if (nodes.isEmpty())
479 {
480 return new HierarchicalConfiguration();
481 }
482
483 final HierarchicalConfiguration parent = this;
484 HierarchicalConfiguration result = new HierarchicalConfiguration()
485 {
486
487 protected Object interpolate(Object value)
488 {
489 return parent.interpolate(value);
490 }
491 };
492 CloneVisitor visitor = new CloneVisitor();
493
494
495 Object value = null;
496 int valueCount = 0;
497 for (Iterator it = nodes.iterator(); it.hasNext();)
498 {
499 ConfigurationNode nd = (ConfigurationNode) it.next();
500 if (nd.getValue() != null)
501 {
502 value = nd.getValue();
503 valueCount++;
504 }
505 nd.visit(visitor);
506
507 for (Iterator it2 = visitor.getClone().getChildren().iterator(); it2
508 .hasNext();)
509 {
510 result.getRootNode().addChild((ConfigurationNode) it2.next());
511 }
512 for (Iterator it2 = visitor.getClone().getAttributes().iterator(); it2
513 .hasNext();)
514 {
515 result.getRootNode().addAttribute(
516 (ConfigurationNode) it2.next());
517 }
518 }
519
520
521 if (valueCount == 1)
522 {
523 result.getRootNode().setValue(value);
524 }
525 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 List nodes = fetchNodeList(key);
579 if (nodes.size() != 1)
580 {
581 throw new IllegalArgumentException(
582 "Passed in key must select exactly one node: " + key);
583 }
584 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 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 List nodes = fetchNodeList(key);
634 List configs = new ArrayList(nodes.size());
635 for (Iterator it = nodes.iterator(); it.hasNext();)
636 {
637 configs.add(createSubnodeConfiguration((ConfigurationNode) it.next()));
638 }
639 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 SubnodeConfiguration result = new SubnodeConfiguration(this, node);
654 registerSubnodeConfiguration(result);
655 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 SubnodeConfiguration result = createSubnodeConfiguration(node);
672 result.setSubnodeKey(subnodeKey);
673 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 fireEvent(EVENT_SUBNODE_CHANGED, null, event, event.isBeforeUpdate());
688 }
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 config.addConfigurationListener(new ConfigurationListener()
701 {
702 public void configurationChanged(ConfigurationEvent event)
703 {
704 subnodeConfigurationChanged(event);
705 }
706 });
707 }
708
709 /***
710 * Checks if the specified key is contained in this configuration. Note that
711 * for this configuration the term "contained" 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 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 fireEvent(EVENT_SET_PROPERTY, key, value, true);
733
734
735 Iterator itNodes = fetchNodeList(key).iterator();
736 Iterator itValues;
737 if (!isDelimiterParsingDisabled())
738 {
739 itValues = PropertyConverter.toIterator(value, getListDelimiter());
740 }
741 else
742 {
743 itValues = new SingletonIterator(value);
744 }
745
746 while (itNodes.hasNext() && itValues.hasNext())
747 {
748 ((ConfigurationNode) itNodes.next()).setValue(itValues.next());
749 }
750
751
752 while (itValues.hasNext())
753 {
754 addPropertyDirect(key, itValues.next());
755 }
756
757
758 while (itNodes.hasNext())
759 {
760 clearNode((ConfigurationNode) itNodes.next());
761 }
762
763 fireEvent(EVENT_SET_PROPERTY, key, value, false);
764 }
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 * "foo" and a property with the key "foo.bar", 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 fireEvent(EVENT_CLEAR_TREE, key, null, true);
777 List nodes = fetchNodeList(key);
778
779 for (Iterator it = nodes.iterator(); it.hasNext();)
780 {
781 removeNode((ConfigurationNode) it.next());
782 }
783 fireEvent(EVENT_CLEAR_TREE, key, nodes, false);
784 }
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 fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
796 List nodes = fetchNodeList(key);
797
798 for (Iterator it = nodes.iterator(); it.hasNext();)
799 {
800 clearNode((ConfigurationNode) it.next());
801 }
802
803 fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);
804 }
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 DefinedKeysVisitor visitor = new DefinedKeysVisitor();
816 getRootNode().visit(visitor);
817
818 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 DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
832 List nodes = fetchNodeList(prefix);
833
834 for (Iterator itNodes = nodes.iterator(); itNodes.hasNext();)
835 {
836 ConfigurationNode node = (ConfigurationNode) itNodes.next();
837 for (Iterator it = node.getChildren().iterator(); it.hasNext();)
838 {
839 ((ConfigurationNode) it.next()).visit(visitor);
840 }
841 for (Iterator it = node.getAttributes().iterator(); it.hasNext();)
842 {
843 ((ConfigurationNode) it.next()).visit(visitor);
844 }
845 }
846
847 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 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 HierarchicalConfiguration copy = (HierarchicalConfiguration) super
877 .clone();
878
879
880 CloneVisitor v = new CloneVisitor();
881 getRootNode().visit(v);
882 copy.setRootNode(v.getClone());
883
884 return copy;
885 }
886 catch (CloneNotSupportedException cex)
887 {
888
889 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 HierarchicalConfiguration c = (HierarchicalConfiguration) clone();
906 c.getRootNode().visit(new ConfigurationNodeVisitorAdapter()
907 {
908 public void visitAfterChildren(ConfigurationNode node)
909 {
910 node.setValue(interpolate(node.getValue()));
911 }
912 });
913 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 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 }
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 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 DefinedVisitor visitor = new DefinedVisitor();
968 node.visit(visitor);
969 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 removeNode((ConfigurationNode) node);
984 }
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 ConfigurationNode parent = node.getParentNode();
996 if (parent != null)
997 {
998 parent.removeChild(node);
999 if (!nodeDefined(parent))
1000 {
1001 removeNode(parent);
1002 }
1003 }
1004 }
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 clearNode((ConfigurationNode) node);
1017 }
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 node.setValue(null);
1028 if (!nodeDefined(node))
1029 {
1030 removeNode(node);
1031 }
1032 }
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 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 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 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 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 ConfigurationNode node = data.getParent();
1116
1117
1118 for (Iterator it = data.getPathNodes().iterator(); it.hasNext();)
1119 {
1120 ConfigurationNode child = createNode((String) it.next());
1121 node.addChild(child);
1122 node = child;
1123 }
1124
1125
1126 ConfigurationNode child = createNode(data.getNewNodeName());
1127 if (data.isAttribute())
1128 {
1129 node.addAttribute(child);
1130 }
1131 else
1132 {
1133 node.addChild(child);
1134 }
1135 return child;
1136 }
1137
1138 /***
1139 * Clears all reference fields in a node structure. A configuration node can
1140 * store a so-called "reference". 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 node.visit(new ConfigurationNodeVisitorAdapter()
1152 {
1153 public void visitBeforeChildren(ConfigurationNode node)
1154 {
1155 node.setReference(null);
1156 }
1157 });
1158 }
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 super();
1180 }
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 super(name);
1190 }
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 super(name, value);
1201 }
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 this(src.getName(), src.getValue());
1213 setReference(src.getReference());
1214 for (Iterator it = src.getChildren().iterator(); it.hasNext();)
1215 {
1216 addChild((ConfigurationNode) it.next());
1217 }
1218 for (Iterator it = src.getAttributes().iterator(); it.hasNext();)
1219 {
1220 addAttribute((ConfigurationNode) it.next());
1221 }
1222 }
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 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 setParentNode(node);
1242 }
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 addChild((ConfigurationNode) node);
1252 }
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 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 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 boolean childrenRemoved = removeChild(name);
1284 boolean attrsRemoved = removeAttribute(name);
1285 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 int length = 0;
1302 if (key != null)
1303 {
1304 length = key.length();
1305 if (getName() != null)
1306 {
1307 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 visitor.visitBeforeChildren(this, key);
1320
1321 for (Iterator it = getChildren().iterator(); it.hasNext()
1322 && !visitor.terminate();)
1323 {
1324 ((Node) it.next()).visit(visitor, key);
1325 }
1326 for (Iterator it = getAttributes().iterator(); it.hasNext()
1327 && !visitor.terminate();)
1328 {
1329 ((Node) it.next()).visit(visitor, key);
1330 }
1331
1332 if (key != null)
1333 {
1334 key.setLength(length);
1335 }
1336 visitor.visitAfterChildren(this, key);
1337 }
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 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 }
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 }
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 return false;
1387 }
1388 }
1389
1390 /***
1391 * A specialized visitor that checks if a node is defined.
1392 * "Defined" in this terms means that the node or at least one of
1393 * its sub nodes is associated with a value.
1394 *
1395 */
1396 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 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 defined = node.getValue() != null;
1420 }
1421
1422 /***
1423 * Returns the defined flag.
1424 *
1425 * @return the defined flag
1426 */
1427 public boolean isDefined()
1428 {
1429 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 {
1450 keyList = new ListOrderedSet();
1451 parentKeys = new Stack();
1452 }
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 this();
1463 parentKeys.push(prefix);
1464 }
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 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 parentKeys.pop();
1485 }
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 String parentKey = parentKeys.isEmpty() ? null
1496 : (String) parentKeys.peek();
1497 String key = getExpressionEngine().nodeKey(node, parentKey);
1498 parentKeys.push(key);
1499 if (node.getValue() != null)
1500 {
1501 keyList.add(key);
1502 }
1503 }
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 {
1523 copyStack = new Stack();
1524 }
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 ConfigurationNode copy = (ConfigurationNode) copyStack.pop();
1534 if (copyStack.isEmpty())
1535 {
1536 result = copy;
1537 }
1538 }
1539
1540 /***
1541 * Visits and copies the specified node.
1542 *
1543 * @param node the node
1544 */
1545 public void visitBeforeChildren(ConfigurationNode node)
1546 {
1547 ConfigurationNode copy = (ConfigurationNode) node.clone();
1548 copy.setParentNode(null);
1549
1550 if (!copyStack.isEmpty())
1551 {
1552 if (node.isAttribute())
1553 {
1554 ((ConfigurationNode) copyStack.peek()).addAttribute(copy);
1555 }
1556 else
1557 {
1558 ((ConfigurationNode) copyStack.peek()).addChild(copy);
1559 }
1560 }
1561
1562 copyStack.push(copy);
1563 }
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 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 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 Collection subNodes = new LinkedList(node.getChildren());
1603 subNodes.addAll(node.getAttributes());
1604 Iterator children = subNodes.iterator();
1605 Node sibling1 = null;
1606 Node nd = null;
1607
1608 while (children.hasNext())
1609 {
1610
1611 do
1612 {
1613 sibling1 = nd;
1614 nd = (Node) children.next();
1615 } while (nd.getReference() != null && children.hasNext());
1616
1617 if (nd.getReference() == null)
1618 {
1619
1620 List newNodes = new LinkedList();
1621 newNodes.add(nd);
1622 while (children.hasNext())
1623 {
1624 nd = (Node) children.next();
1625 if (nd.getReference() == null)
1626 {
1627 newNodes.add(nd);
1628 }
1629 else
1630 {
1631 break;
1632 }
1633 }
1634
1635
1636 Node sibling2 = (nd.getReference() == null) ? null : nd;
1637 for (Iterator it = newNodes.iterator(); it.hasNext();)
1638 {
1639 Node insertNode = (Node) it.next();
1640 if (insertNode.getReference() == null)
1641 {
1642 Object ref = insert(insertNode, node, sibling1, sibling2);
1643 if (ref != null)
1644 {
1645 insertNode.setReference(ref);
1646 }
1647 sibling1 = insertNode;
1648 }
1649 }
1650 }
1651 }
1652 }
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 }