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