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