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.Collections;
024 import java.util.Iterator;
025 import java.util.LinkedHashSet;
026 import java.util.LinkedList;
027 import java.util.List;
028 import java.util.Set;
029 import java.util.Stack;
030
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} 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} 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} can be used to find out the name of the
091 * first table. In opposite {@code tables.table.name} would return a
092 * collection with the names of all available tables. Similarly the key
093 * {@code tables.table(1).fields.field.name} returns a collection with
094 * the names of all fields of the second table. If another index is added after
095 * the {@code field} element, a single field can be accessed:
096 * {@code tables.table(1).fields.field(0).name}.</p><p>There is a
097 * {@code getMaxIndex()} 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}
105 * instance is created. With the {@code setExpressionEngine()} method a
106 * different expression engine can be set. For instance with
107 * {@link org.apache.commons.configuration.tree.xpath.XPathExpressionEngine}
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()} 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()} 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}
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 1206305 2011-11-25 20:37:52Z 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}.
170 */
171 public HierarchicalConfiguration()
172 {
173 setRootNode(new Node());
174 }
175
176 /**
177 * Creates a new instance of {@code HierarchicalConfiguration} 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 * {@link #getRootNode()} method instead, which operates on
211 * the preferred data type {@code ConfigurationNode}.
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 * {@link #setRootNode(ConfigurationNode)} method instead,
230 * which operates on the preferred data type {@code ConfigurationNode}.
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<ConfigurationNode> nodes = fetchNodeList(key);
345
346 if (nodes.size() == 0)
347 {
348 return null;
349 }
350 else
351 {
352 List<Object> list = new ArrayList<Object>();
353 for (ConfigurationNode node : nodes)
354 {
355 if (node.getValue() != null)
356 {
357 list.add(node.getValue());
358 }
359 }
360
361 if (list.size() < 1)
362 {
363 return null;
364 }
365 else
366 {
367 return (list.size() == 1) ? list.get(0) : list;
368 }
369 }
370 }
371
372 /**
373 * Adds the property with the specified key. This task will be delegated to
374 * the associated {@code ExpressionEngine}, so the passed in key
375 * must match the requirements of this implementation.
376 *
377 * @param key the key of the new property
378 * @param obj the value of the new property
379 */
380 @Override
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()}, 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}
394 * object to this object. (However be aware that a
395 * {@code ConfigurationNode} 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()}.) 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} objects to be
407 * added
408 */
409 public void addNodes(String key, Collection<? extends ConfigurationNode> 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<ConfigurationNode> target = fetchNodeList(key);
419 if (target.size() == 1)
420 {
421 // existing unique key
422 parent = 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 (ConfigurationNode child : nodes)
438 {
439 if (child.isAttribute())
440 {
441 parent.addAttribute(child);
442 }
443 else
444 {
445 parent.addChild(child);
446 }
447 clearReferences(child);
448 }
449 fireEvent(EVENT_ADD_NODES, key, nodes, false);
450 }
451
452 /**
453 * Checks if this configuration is empty. Empty means that there are no keys
454 * with any values, though there can be some (empty) nodes.
455 *
456 * @return a flag if this configuration is empty
457 */
458 public boolean isEmpty()
459 {
460 return !nodeDefined(getRootNode());
461 }
462
463 /**
464 * Creates a new {@code Configuration} object containing all keys
465 * that start with the specified prefix. This implementation will return a
466 * {@code HierarchicalConfiguration} object so that the structure of
467 * the keys will be saved. The nodes selected by the prefix (it is possible
468 * that multiple nodes are selected) are mapped to the root node of the
469 * returned configuration, i.e. their children and attributes will become
470 * children and attributes of the new root node. However a value of the root
471 * node is only set if exactly one of the selected nodes contain a value (if
472 * multiple nodes have a value, there is simply no way to decide how these
473 * values are merged together). Note that the returned
474 * {@code Configuration} object is not connected to its source
475 * configuration: updates on the source configuration are not reflected in
476 * the subset and vice versa.
477 *
478 * @param prefix the prefix of the keys for the subset
479 * @return a new configuration object representing the selected subset
480 */
481 @Override
482 public Configuration subset(String prefix)
483 {
484 Collection<ConfigurationNode> 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 @Override
495 protected Object interpolate(Object value)
496 {
497 return parent.interpolate(value);
498 }
499 };
500 CloneVisitor visitor = new CloneVisitor();
501
502 // Initialize the new root node
503 Object value = null;
504 int valueCount = 0;
505 for (ConfigurationNode nd : nodes)
506 {
507 if (nd.getValue() != null)
508 {
509 value = nd.getValue();
510 valueCount++;
511 }
512 nd.visit(visitor);
513
514 for (ConfigurationNode c : visitor.getClone().getChildren())
515 {
516 result.getRootNode().addChild(c);
517 }
518 for (ConfigurationNode attr : visitor.getClone().getAttributes())
519 {
520 result.getRootNode().addAttribute(attr);
521 }
522 }
523
524 // Determine the value of the new root
525 if (valueCount == 1)
526 {
527 result.getRootNode().setValue(value);
528 }
529 return (result.isEmpty()) ? new HierarchicalConfiguration() : result;
530 }
531
532 /**
533 * <p>
534 * Returns a hierarchical subnode configuration object that wraps the
535 * configuration node specified by the given key. This method provides an
536 * easy means of accessing sub trees of a hierarchical configuration. In the
537 * returned configuration the sub tree can directly be accessed, it becomes
538 * the root node of this configuration. Because of this the passed in key
539 * must select exactly one configuration node; otherwise an
540 * {@code IllegalArgumentException} will be thrown.
541 * </p>
542 * <p>
543 * The difference between this method and the
544 * {@link #subset(String)} method is that
545 * {@code subset()} supports arbitrary subsets of configuration nodes
546 * while {@code configurationAt()} only returns a single sub tree.
547 * Please refer to the documentation of the
548 * {@code SubnodeConfiguration} class to obtain further information
549 * about subnode configurations and when they should be used.
550 * </p>
551 * <p>
552 * With the {@code supportUpdate} flag the behavior of the returned
553 * {@code SubnodeConfiguration} regarding updates of its parent
554 * configuration can be determined. A subnode configuration operates on the
555 * same nodes as its parent, so changes at one configuration are normally
556 * directly visible for the other configuration. There are however changes
557 * of the parent configuration, which are not recognized by the subnode
558 * configuration per default. An example for this is a reload operation (for
559 * file-based configurations): Here the complete node set of the parent
560 * configuration is replaced, but the subnode configuration still references
561 * the old nodes. If such changes should be detected by the subnode
562 * configuration, the {@code supportUpdates} flag must be set to
563 * <b>true</b>. This causes the subnode configuration to reevaluate the key
564 * used for its creation each time it is accessed. This guarantees that the
565 * subnode configuration always stays in sync with its key, even if the
566 * parent configuration's data significantly changes. If such a change
567 * makes the key invalid - because it now no longer points to exactly one
568 * node -, the subnode configuration is not reconstructed, but keeps its
569 * old data. It is then quasi detached from its parent.
570 * </p>
571 *
572 * @param key the key that selects the sub tree
573 * @param supportUpdates a flag whether the returned subnode configuration
574 * should be able to handle updates of its parent
575 * @return a hierarchical configuration that contains this sub tree
576 * @see SubnodeConfiguration
577 * @since 1.5
578 */
579 public SubnodeConfiguration configurationAt(String key,
580 boolean supportUpdates)
581 {
582 List<ConfigurationNode> nodes = fetchNodeList(key);
583 if (nodes.size() != 1)
584 {
585 throw new IllegalArgumentException(
586 "Passed in key must select exactly one node: " + key);
587 }
588 return supportUpdates ? createSubnodeConfiguration(
589 nodes.get(0), key)
590 : createSubnodeConfiguration(nodes.get(0));
591 }
592
593 /**
594 * Returns a hierarchical subnode configuration for the node specified by
595 * the given key. This is a short form for {@code configurationAt(key,
596 * <b>false</b>)}.
597 *
598 * @param key the key that selects the sub tree
599 * @return a hierarchical configuration that contains this sub tree
600 * @see SubnodeConfiguration
601 * @since 1.3
602 */
603 public SubnodeConfiguration configurationAt(String key)
604 {
605 return configurationAt(key, false);
606 }
607
608 /**
609 * Returns a list of sub configurations for all configuration nodes selected
610 * by the given key. This method will evaluate the passed in key (using the
611 * current {@code ExpressionEngine}) and then create a subnode
612 * configuration for each returned node (like
613 * {@link #configurationAt(String)}}). This is especially
614 * useful when dealing with list-like structures. As an example consider the
615 * configuration that contains data about database tables and their fields.
616 * If you need access to all fields of a certain table, you can simply do
617 *
618 * <pre>
619 * List fields = config.configurationsAt("tables.table(0).fields.field");
620 * for(Iterator it = fields.iterator(); it.hasNext();)
621 * {
622 * HierarchicalConfiguration sub = (HierarchicalConfiguration) it.next();
623 * // now the children and attributes of the field node can be
624 * // directly accessed
625 * String fieldName = sub.getString("name");
626 * String fieldType = sub.getString("type");
627 * ...
628 * </pre>
629 *
630 * @param key the key for selecting the desired nodes
631 * @return a list with hierarchical configuration objects; each
632 * configuration represents one of the nodes selected by the passed in key
633 * @since 1.3
634 */
635 public List<HierarchicalConfiguration> configurationsAt(String key)
636 {
637 List<ConfigurationNode> nodes = fetchNodeList(key);
638 List<HierarchicalConfiguration> configs = new ArrayList<HierarchicalConfiguration>(nodes.size());
639 for (ConfigurationNode node : nodes)
640 {
641 configs.add(createSubnodeConfiguration(node));
642 }
643 return configs;
644 }
645
646 /**
647 * Creates a subnode configuration for the specified node. This method is
648 * called by {@code configurationAt()} and
649 * {@code configurationsAt()}.
650 *
651 * @param node the node, for which a subnode configuration is to be created
652 * @return the configuration for the given node
653 * @since 1.3
654 */
655 protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node)
656 {
657 SubnodeConfiguration result = new SubnodeConfiguration(this, node);
658 registerSubnodeConfiguration(result);
659 return result;
660 }
661
662 /**
663 * Creates a new subnode configuration for the specified node and sets its
664 * construction key. A subnode configuration created this way will be aware
665 * of structural changes of its parent.
666 *
667 * @param node the node, for which a subnode configuration is to be created
668 * @param subnodeKey the key used to construct the configuration
669 * @return the configuration for the given node
670 * @since 1.5
671 */
672 protected SubnodeConfiguration createSubnodeConfiguration(
673 ConfigurationNode node, String subnodeKey)
674 {
675 SubnodeConfiguration result = createSubnodeConfiguration(node);
676 result.setSubnodeKey(subnodeKey);
677 return result;
678 }
679
680 /**
681 * This method is always called when a subnode configuration created from
682 * this configuration has been modified. This implementation transforms the
683 * received event into an event of type {@code EVENT_SUBNODE_CHANGED}
684 * and notifies the registered listeners.
685 *
686 * @param event the event describing the change
687 * @since 1.5
688 */
689 protected void subnodeConfigurationChanged(ConfigurationEvent event)
690 {
691 fireEvent(EVENT_SUBNODE_CHANGED, null, event, event.isBeforeUpdate());
692 }
693
694 /**
695 * Registers this instance at the given subnode configuration. This
696 * implementation will register a change listener, so that modifications of
697 * the subnode configuration can be tracked.
698 *
699 * @param config the subnode configuration
700 * @since 1.5
701 */
702 void registerSubnodeConfiguration(SubnodeConfiguration config)
703 {
704 config.addConfigurationListener(new ConfigurationListener()
705 {
706 public void configurationChanged(ConfigurationEvent event)
707 {
708 subnodeConfigurationChanged(event);
709 }
710 });
711 }
712
713 /**
714 * Checks if the specified key is contained in this configuration. Note that
715 * for this configuration the term "contained" means that the key
716 * has an associated value. If there is a node for this key that has no
717 * value but children (either defined or undefined), this method will still
718 * return <b>false </b>.
719 *
720 * @param key the key to be chekced
721 * @return a flag if this key is contained in this configuration
722 */
723 public boolean containsKey(String key)
724 {
725 return getProperty(key) != null;
726 }
727
728 /**
729 * Sets the value of the specified property.
730 *
731 * @param key the key of the property to set
732 * @param value the new value of this property
733 */
734 @Override
735 public void setProperty(String key, Object value)
736 {
737 fireEvent(EVENT_SET_PROPERTY, key, value, true);
738
739 // Update the existing nodes for this property
740 Iterator<ConfigurationNode> itNodes = fetchNodeList(key).iterator();
741 Iterator<?> itValues;
742 if (!isDelimiterParsingDisabled())
743 {
744 itValues = PropertyConverter.toIterator(value, getListDelimiter());
745 }
746 else
747 {
748 itValues = Collections.singleton(value).iterator();
749 }
750
751 while (itNodes.hasNext() && itValues.hasNext())
752 {
753 ((ConfigurationNode) itNodes.next()).setValue(itValues.next());
754 }
755
756 // Add additional nodes if necessary
757 while (itValues.hasNext())
758 {
759 addPropertyDirect(key, itValues.next());
760 }
761
762 // Remove remaining nodes
763 while (itNodes.hasNext())
764 {
765 clearNode((ConfigurationNode) itNodes.next());
766 }
767
768 fireEvent(EVENT_SET_PROPERTY, key, value, false);
769 }
770
771 /**
772 * Clears this configuration. This is a more efficient implementation than
773 * the one inherited from the base class. It directly removes all data from
774 * the root node.
775 */
776 @Override
777 public void clear()
778 {
779 fireEvent(EVENT_CLEAR, null, null, true);
780 getRootNode().removeAttributes();
781 getRootNode().removeChildren();
782 getRootNode().setValue(null);
783 fireEvent(EVENT_CLEAR, null, null, false);
784 }
785
786 /**
787 * Removes all values of the property with the given name and of keys that
788 * start with this name. So if there is a property with the key
789 * "foo" and a property with the key "foo.bar", a call
790 * of {@code clearTree("foo")} would remove both properties.
791 *
792 * @param key the key of the property to be removed
793 */
794 public void clearTree(String key)
795 {
796 fireEvent(EVENT_CLEAR_TREE, key, null, true);
797 List<ConfigurationNode> nodes = fetchNodeList(key);
798
799 for (ConfigurationNode node : nodes)
800 {
801 removeNode(node);
802 }
803 fireEvent(EVENT_CLEAR_TREE, key, nodes, false);
804 }
805
806 /**
807 * Removes the property with the given key. Properties with names that start
808 * with the given key (i.e. properties below the specified key in the
809 * hierarchy) won't be affected.
810 *
811 * @param key the key of the property to be removed
812 */
813 @Override
814 public void clearProperty(String key)
815 {
816 fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);
817 List<ConfigurationNode> nodes = fetchNodeList(key);
818
819 for (ConfigurationNode node : nodes)
820 {
821 clearNode(node);
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<String> 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 @Override
854 public Iterator<String> getKeys(String prefix)
855 {
856 DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
857 if (containsKey(prefix))
858 {
859 // explicitly add the prefix
860 visitor.getKeyList().add(prefix);
861 }
862
863 List<ConfigurationNode> nodes = fetchNodeList(prefix);
864
865 for (ConfigurationNode node : nodes)
866 {
867 for (ConfigurationNode c : node.getChildren())
868 {
869 c.visit(visitor);
870 }
871 for (ConfigurationNode attr : node.getAttributes())
872 {
873 attr.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 @Override
903 public Object clone()
904 {
905 try
906 {
907 HierarchicalConfiguration copy = (HierarchicalConfiguration) super
908 .clone();
909
910 // clone the nodes, too
911 CloneVisitor v = new CloneVisitor();
912 getRootNode().visit(v);
913 copy.setRootNode(v.getClone());
914
915 return copy;
916 }
917 catch (CloneNotSupportedException cex)
918 {
919 // should not happen
920 throw new ConfigurationRuntimeException(cex);
921 }
922 }
923
924 /**
925 * Returns a configuration with the same content as this configuration, but
926 * with all variables replaced by their actual values. This implementation
927 * is specific for hierarchical configurations. It clones the current
928 * configuration and runs a specialized visitor on the clone, which performs
929 * interpolation on the single configuration nodes.
930 *
931 * @return a configuration with all variables interpolated
932 * @since 1.5
933 */
934 @Override
935 public Configuration interpolatedConfiguration()
936 {
937 HierarchicalConfiguration c = (HierarchicalConfiguration) clone();
938 c.getRootNode().visit(new ConfigurationNodeVisitorAdapter()
939 {
940 @Override
941 public void visitAfterChildren(ConfigurationNode node)
942 {
943 node.setValue(interpolate(node.getValue()));
944 }
945 });
946 return c;
947 }
948
949 /**
950 * Helper method for fetching a list of all nodes that are addressed by the
951 * specified key.
952 *
953 * @param key the key
954 * @return a list with all affected nodes (never <b>null </b>)
955 */
956 protected List<ConfigurationNode> fetchNodeList(String key)
957 {
958 return getExpressionEngine().query(getRootNode(), key);
959 }
960
961 /**
962 * Recursive helper method for fetching a property. This method processes
963 * all facets of a configuration key, traverses the tree of properties and
964 * fetches the the nodes of all matching properties.
965 *
966 * @param keyPart the configuration key iterator
967 * @param node the actual node
968 * @param nodes here the found nodes are stored
969 * @deprecated Property keys are now evaluated by the expression engine
970 * associated with the configuration; this method will no longer be called.
971 * If you want to modify the way properties are looked up, consider
972 * implementing you own {@code ExpressionEngine} implementation.
973 */
974 @Deprecated
975 protected void findPropertyNodes(ConfigurationKey.KeyIterator keyPart,
976 Node node, Collection<ConfigurationNode> nodes)
977 {
978 }
979
980 /**
981 * Checks if the specified node is defined.
982 *
983 * @param node the node to be checked
984 * @return a flag if this node is defined
985 * @deprecated Use the method {@link #nodeDefined(ConfigurationNode)}
986 * instead.
987 */
988 @Deprecated
989 protected boolean nodeDefined(Node node)
990 {
991 return nodeDefined((ConfigurationNode) node);
992 }
993
994 /**
995 * Checks if the specified node is defined.
996 *
997 * @param node the node to be checked
998 * @return a flag if this node is defined
999 */
1000 protected boolean nodeDefined(ConfigurationNode node)
1001 {
1002 DefinedVisitor visitor = new DefinedVisitor();
1003 node.visit(visitor);
1004 return visitor.isDefined();
1005 }
1006
1007 /**
1008 * Removes the specified node from this configuration. This method ensures
1009 * that parent nodes that become undefined by this operation are also
1010 * removed.
1011 *
1012 * @param node the node to be removed
1013 * @deprecated Use the method {@link #removeNode(ConfigurationNode)}
1014 * instead.
1015 */
1016 @Deprecated
1017 protected void removeNode(Node node)
1018 {
1019 removeNode((ConfigurationNode) node);
1020 }
1021
1022 /**
1023 * Removes the specified node from this configuration. This method ensures
1024 * that parent nodes that become undefined by this operation are also
1025 * removed.
1026 *
1027 * @param node the node to be removed
1028 */
1029 protected void removeNode(ConfigurationNode node)
1030 {
1031 ConfigurationNode parent = node.getParentNode();
1032 if (parent != null)
1033 {
1034 parent.removeChild(node);
1035 if (!nodeDefined(parent))
1036 {
1037 removeNode(parent);
1038 }
1039 }
1040 }
1041
1042 /**
1043 * Clears the value of the specified node. If the node becomes undefined by
1044 * this operation, it is removed from the hierarchy.
1045 *
1046 * @param node the node to be cleared
1047 * @deprecated Use the method {@link #clearNode(ConfigurationNode)}
1048 * instead
1049 */
1050 @Deprecated
1051 protected void clearNode(Node node)
1052 {
1053 clearNode((ConfigurationNode) node);
1054 }
1055
1056 /**
1057 * Clears the value of the specified node. If the node becomes undefined by
1058 * this operation, it is removed from the hierarchy.
1059 *
1060 * @param node the node to be cleared
1061 */
1062 protected void clearNode(ConfigurationNode node)
1063 {
1064 node.setValue(null);
1065 if (!nodeDefined(node))
1066 {
1067 removeNode(node);
1068 }
1069 }
1070
1071 /**
1072 * Returns a reference to the parent node of an add operation. Nodes for new
1073 * properties can be added as children of this node. If the path for the
1074 * specified key does not exist so far, it is created now.
1075 *
1076 * @param keyIt the iterator for the key of the new property
1077 * @param startNode the node to start the search with
1078 * @return the parent node for the add operation
1079 * @deprecated Adding new properties is now to a major part delegated to the
1080 * {@code ExpressionEngine} associated with this configuration instance.
1081 * This method will no longer be called. Developers who want to modify the
1082 * process of adding new properties should consider implementing their own
1083 * expression engine.
1084 */
1085 @Deprecated
1086 protected Node fetchAddNode(ConfigurationKey.KeyIterator keyIt, Node startNode)
1087 {
1088 return null;
1089 }
1090
1091 /**
1092 * Finds the last existing node for an add operation. This method traverses
1093 * the configuration tree along the specified key. The last existing node on
1094 * this path is returned.
1095 *
1096 * @param keyIt the key iterator
1097 * @param node the actual node
1098 * @return the last existing node on the given path
1099 * @deprecated Adding new properties is now to a major part delegated to the
1100 * {@code ExpressionEngine} associated with this configuration instance.
1101 * This method will no longer be called. Developers who want to modify the
1102 * process of adding new properties should consider implementing their own
1103 * expression engine.
1104 */
1105 @Deprecated
1106 protected Node findLastPathNode(ConfigurationKey.KeyIterator keyIt, Node node)
1107 {
1108 return null;
1109 }
1110
1111 /**
1112 * Creates the missing nodes for adding a new property. This method ensures
1113 * that there are corresponding nodes for all components of the specified
1114 * configuration key.
1115 *
1116 * @param keyIt the key iterator
1117 * @param root the base node of the path to be created
1118 * @return the last node of the path
1119 * @deprecated Adding new properties is now to a major part delegated to the
1120 * {@code ExpressionEngine} associated with this configuration instance.
1121 * This method will no longer be called. Developers who want to modify the
1122 * process of adding new properties should consider implementing their own
1123 * expression engine.
1124 */
1125 @Deprecated
1126 protected Node createAddPath(ConfigurationKey.KeyIterator keyIt, Node root)
1127 {
1128 return null;
1129 }
1130
1131 /**
1132 * Creates a new {@code Node} object with the specified name. This
1133 * method can be overloaded in derived classes if a specific node type is
1134 * needed. This base implementation always returns a new object of the
1135 * {@code Node} class.
1136 *
1137 * @param name the name of the new node
1138 * @return the new node
1139 */
1140 protected Node createNode(String name)
1141 {
1142 return new Node(name);
1143 }
1144
1145 /**
1146 * Helper method for processing a node add data object obtained from the
1147 * expression engine. This method will create all new nodes.
1148 *
1149 * @param data the data object
1150 * @return the new node
1151 * @since 1.3
1152 */
1153 private ConfigurationNode processNodeAddData(NodeAddData data)
1154 {
1155 ConfigurationNode node = data.getParent();
1156
1157 // Create missing nodes on the path
1158 for (String name : data.getPathNodes())
1159 {
1160 ConfigurationNode child = createNode(name);
1161 node.addChild(child);
1162 node = child;
1163 }
1164
1165 // Add new target node
1166 ConfigurationNode child = createNode(data.getNewNodeName());
1167 if (data.isAttribute())
1168 {
1169 node.addAttribute(child);
1170 }
1171 else
1172 {
1173 node.addChild(child);
1174 }
1175 return child;
1176 }
1177
1178 /**
1179 * Clears all reference fields in a node structure. A configuration node can
1180 * store a so-called "reference". The meaning of this data is
1181 * determined by a concrete sub class. Typically such references are
1182 * specific for a configuration instance. If this instance is cloned or
1183 * copied, they must be cleared. This can be done using this method.
1184 *
1185 * @param node the root node of the node hierarchy, in which the references
1186 * are to be cleared
1187 * @since 1.4
1188 */
1189 protected static void clearReferences(ConfigurationNode node)
1190 {
1191 node.visit(new ConfigurationNodeVisitorAdapter()
1192 {
1193 @Override
1194 public void visitBeforeChildren(ConfigurationNode node)
1195 {
1196 node.setReference(null);
1197 }
1198 });
1199 }
1200
1201 /**
1202 * Transforms the specified object into a Node. This method treats view
1203 * nodes in a special way. This is necessary because ViewNode does not
1204 * extend HierarchicalConfiguration.Node; thus the API for the node visitor
1205 * is slightly different. Therefore a view node is transformed into a
1206 * special compatibility Node object.
1207 *
1208 * @param obj the original node object
1209 * @return the node to be used
1210 */
1211 private static Node getNodeFor(Object obj)
1212 {
1213 Node nd;
1214 if (obj instanceof ViewNode)
1215 {
1216 final ViewNode viewNode = (ViewNode) obj;
1217 nd = new Node(viewNode)
1218 {
1219 @Override
1220 public void setReference(Object reference)
1221 {
1222 super.setReference(reference);
1223 // also set the reference at the original node
1224 viewNode.setReference(reference);
1225 }
1226 };
1227 }
1228 else
1229 {
1230 nd = (Node) obj;
1231 }
1232 return nd;
1233 }
1234
1235 /**
1236 * A data class for storing (hierarchical) property information. A property
1237 * can have a value and an arbitrary number of child properties. From
1238 * version 1.3 on this class is only a thin wrapper over the
1239 * {@link org.apache.commons.configuration.tree.DefaultConfigurationNode DefaultconfigurationNode}
1240 * class that exists mainly for the purpose of backwards compatibility.
1241 */
1242 public static class Node extends DefaultConfigurationNode implements Serializable
1243 {
1244 /**
1245 * The serial version UID.
1246 */
1247 private static final long serialVersionUID = -6357500633536941775L;
1248
1249 /**
1250 * Creates a new instance of {@code Node}.
1251 */
1252 public Node()
1253 {
1254 super();
1255 }
1256
1257 /**
1258 * Creates a new instance of {@code Node} and sets the name.
1259 *
1260 * @param name the node's name
1261 */
1262 public Node(String name)
1263 {
1264 super(name);
1265 }
1266
1267 /**
1268 * Creates a new instance of {@code Node} and sets the name and the value.
1269 *
1270 * @param name the node's name
1271 * @param value the value
1272 */
1273 public Node(String name, Object value)
1274 {
1275 super(name, value);
1276 }
1277
1278 /**
1279 * Creates a new instance of {@code Node} based on the given
1280 * source node. All properties of the source node, including its
1281 * children and attributes, will be copied.
1282 *
1283 * @param src the node to be copied
1284 */
1285 public Node(ConfigurationNode src)
1286 {
1287 this(src.getName(), src.getValue());
1288 setReference(src.getReference());
1289 for (ConfigurationNode nd : src.getChildren())
1290 {
1291 // Don't change the parent node
1292 ConfigurationNode parent = nd.getParentNode();
1293 addChild(nd);
1294 nd.setParentNode(parent);
1295 }
1296
1297 for (ConfigurationNode nd : src.getAttributes())
1298 {
1299 // Don't change the parent node
1300 ConfigurationNode parent = nd.getParentNode();
1301 addAttribute(nd);
1302 nd.setParentNode(parent);
1303 }
1304 }
1305
1306 /**
1307 * Returns the parent of this node.
1308 *
1309 * @return this node's parent (can be <b>null</b>)
1310 */
1311 public Node getParent()
1312 {
1313 return (Node) getParentNode();
1314 }
1315
1316 /**
1317 * Sets the parent of this node.
1318 *
1319 * @param node the parent node
1320 */
1321 public void setParent(Node node)
1322 {
1323 setParentNode(node);
1324 }
1325
1326 /**
1327 * Adds the given node to the children of this node.
1328 *
1329 * @param node the child to be added
1330 */
1331 public void addChild(Node node)
1332 {
1333 addChild((ConfigurationNode) node);
1334 }
1335
1336 /**
1337 * Returns a flag whether this node has child elements.
1338 *
1339 * @return <b>true</b> if there is a child node, <b>false</b> otherwise
1340 */
1341 public boolean hasChildren()
1342 {
1343 return getChildrenCount() > 0 || getAttributeCount() > 0;
1344 }
1345
1346 /**
1347 * Removes the specified child from this node.
1348 *
1349 * @param child the child node to be removed
1350 * @return a flag if the child could be found
1351 */
1352 public boolean remove(Node child)
1353 {
1354 return child.isAttribute() ? removeAttribute(child) : removeChild(child);
1355 }
1356
1357 /**
1358 * Removes all children with the given name.
1359 *
1360 * @param name the name of the children to be removed
1361 * @return a flag if children with this name existed
1362 */
1363 public boolean remove(String name)
1364 {
1365 boolean childrenRemoved = removeChild(name);
1366 boolean attrsRemoved = removeAttribute(name);
1367 return childrenRemoved || attrsRemoved;
1368 }
1369
1370 /**
1371 * A generic method for traversing this node and all of its children.
1372 * This method sends the passed in visitor to this node and all of its
1373 * children.
1374 *
1375 * @param visitor the visitor
1376 * @param key here a configuration key with the name of the root node of
1377 * the iteration can be passed; if this key is not <b>null </b>, the
1378 * full paths to the visited nodes are builded and passed to the
1379 * visitor's {@code visit()} methods
1380 */
1381 public void visit(NodeVisitor visitor, ConfigurationKey key)
1382 {
1383 int length = 0;
1384 if (key != null)
1385 {
1386 length = key.length();
1387 if (getName() != null)
1388 {
1389 key
1390 .append(StringUtils
1391 .replace(
1392 isAttribute() ? ConfigurationKey
1393 .constructAttributeKey(getName())
1394 : getName(),
1395 String
1396 .valueOf(ConfigurationKey.PROPERTY_DELIMITER),
1397 ConfigurationKey.ESCAPED_DELIMITER));
1398 }
1399 }
1400
1401 visitor.visitBeforeChildren(this, key);
1402
1403 for (Iterator<ConfigurationNode> it = getChildren().iterator(); it.hasNext()
1404 && !visitor.terminate();)
1405 {
1406 Object obj = it.next();
1407 getNodeFor(obj).visit(visitor, key);
1408 }
1409 for (Iterator<ConfigurationNode> it = getAttributes().iterator(); it.hasNext()
1410 && !visitor.terminate();)
1411 {
1412 Object obj = it.next();
1413 getNodeFor(obj).visit(visitor, key);
1414 }
1415
1416 visitor.visitAfterChildren(this, key);
1417 if (key != null)
1418 {
1419 key.setLength(length);
1420 }
1421 }
1422 }
1423
1424 /**
1425 * <p>Definition of a visitor class for traversing a node and all of its
1426 * children.</p><p>This class defines the interface of a visitor for
1427 * {@code Node} objects and provides a default implementation. The
1428 * method {@code visit()} of {@code Node} implements a generic
1429 * iteration algorithm based on the <em>Visitor</em> pattern. By providing
1430 * different implementations of visitors it is possible to collect different
1431 * data during the iteration process.</p>
1432 *
1433 */
1434 public static class NodeVisitor
1435 {
1436 /**
1437 * Visits the specified node. This method is called during iteration for
1438 * each node before its children have been visited.
1439 *
1440 * @param node the actual node
1441 * @param key the key of this node (may be <b>null </b>)
1442 */
1443 public void visitBeforeChildren(Node node, ConfigurationKey key)
1444 {
1445 }
1446
1447 /**
1448 * Visits the specified node after its children have been processed.
1449 * This gives a visitor the opportunity of collecting additional data
1450 * after the child nodes have been visited.
1451 *
1452 * @param node the node to be visited
1453 * @param key the key of this node (may be <b>null </b>)
1454 */
1455 public void visitAfterChildren(Node node, ConfigurationKey key)
1456 {
1457 }
1458
1459 /**
1460 * Returns a flag that indicates if iteration should be stopped. This
1461 * method is called after each visited node. It can be useful for
1462 * visitors that search a specific node. If this node is found, the
1463 * whole process can be stopped. This base implementation always returns
1464 * <b>false </b>.
1465 *
1466 * @return a flag if iteration should be stopped
1467 */
1468 public boolean terminate()
1469 {
1470 return false;
1471 }
1472 }
1473
1474 /**
1475 * A specialized visitor that checks if a node is defined.
1476 * "Defined" in this terms means that the node or at least one of
1477 * its sub nodes is associated with a value.
1478 *
1479 */
1480 static class DefinedVisitor extends ConfigurationNodeVisitorAdapter
1481 {
1482 /** Stores the defined flag. */
1483 private boolean defined;
1484
1485 /**
1486 * Checks if iteration should be stopped. This can be done if the first
1487 * defined node is found.
1488 *
1489 * @return a flag if iteration should be stopped
1490 */
1491 @Override
1492 public boolean terminate()
1493 {
1494 return isDefined();
1495 }
1496
1497 /**
1498 * Visits the node. Checks if a value is defined.
1499 *
1500 * @param node the actual node
1501 */
1502 @Override
1503 public void visitBeforeChildren(ConfigurationNode node)
1504 {
1505 defined = node.getValue() != null;
1506 }
1507
1508 /**
1509 * Returns the defined flag.
1510 *
1511 * @return the defined flag
1512 */
1513 public boolean isDefined()
1514 {
1515 return defined;
1516 }
1517 }
1518
1519 /**
1520 * A specialized visitor that fills a list with keys that are defined in a
1521 * node hierarchy.
1522 */
1523 class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter
1524 {
1525 /** Stores the list to be filled. */
1526 private Set<String> keyList;
1527
1528 /** A stack with the keys of the already processed nodes. */
1529 private Stack<String> parentKeys;
1530
1531 /**
1532 * Default constructor.
1533 */
1534 public DefinedKeysVisitor()
1535 {
1536 keyList = new LinkedHashSet<String>();
1537 parentKeys = new Stack<String>();
1538 }
1539
1540 /**
1541 * Creates a new {@code DefinedKeysVisitor} instance and sets the
1542 * prefix for the keys to fetch.
1543 *
1544 * @param prefix the prefix
1545 */
1546 public DefinedKeysVisitor(String prefix)
1547 {
1548 this();
1549 parentKeys.push(prefix);
1550 }
1551
1552 /**
1553 * Returns the list with all defined keys.
1554 *
1555 * @return the list with the defined keys
1556 */
1557 public Set<String> getKeyList()
1558 {
1559 return keyList;
1560 }
1561
1562 /**
1563 * Visits the node after its children has been processed. Removes this
1564 * node's key from the stack.
1565 *
1566 * @param node the node
1567 */
1568 @Override
1569 public void visitAfterChildren(ConfigurationNode node)
1570 {
1571 parentKeys.pop();
1572 }
1573
1574 /**
1575 * Visits the specified node. If this node has a value, its key is added
1576 * to the internal list.
1577 *
1578 * @param node the node to be visited
1579 */
1580 @Override
1581 public void visitBeforeChildren(ConfigurationNode node)
1582 {
1583 String parentKey = parentKeys.isEmpty() ? null
1584 : (String) parentKeys.peek();
1585 String key = getExpressionEngine().nodeKey(node, parentKey);
1586 parentKeys.push(key);
1587 if (node.getValue() != null)
1588 {
1589 keyList.add(key);
1590 }
1591 }
1592 }
1593
1594 /**
1595 * A specialized visitor that is able to create a deep copy of a node
1596 * hierarchy.
1597 */
1598 static class CloneVisitor extends ConfigurationNodeVisitorAdapter
1599 {
1600 /** A stack with the actual object to be copied. */
1601 private Stack<ConfigurationNode> copyStack;
1602
1603 /** Stores the result of the clone process. */
1604 private ConfigurationNode result;
1605
1606 /**
1607 * Creates a new instance of {@code CloneVisitor}.
1608 */
1609 public CloneVisitor()
1610 {
1611 copyStack = new Stack<ConfigurationNode>();
1612 }
1613
1614 /**
1615 * Visits the specified node after its children have been processed.
1616 *
1617 * @param node the node
1618 */
1619 @Override
1620 public void visitAfterChildren(ConfigurationNode node)
1621 {
1622 ConfigurationNode copy = copyStack.pop();
1623 if (copyStack.isEmpty())
1624 {
1625 result = copy;
1626 }
1627 }
1628
1629 /**
1630 * Visits and copies the specified node.
1631 *
1632 * @param node the node
1633 */
1634 @Override
1635 public void visitBeforeChildren(ConfigurationNode node)
1636 {
1637 ConfigurationNode copy = (ConfigurationNode) node.clone();
1638 copy.setParentNode(null);
1639
1640 if (!copyStack.isEmpty())
1641 {
1642 if (node.isAttribute())
1643 {
1644 copyStack.peek().addAttribute(copy);
1645 }
1646 else
1647 {
1648 copyStack.peek().addChild(copy);
1649 }
1650 }
1651
1652 copyStack.push(copy);
1653 }
1654
1655 /**
1656 * Returns the result of the clone process. This is the root node of the
1657 * cloned node hierarchy.
1658 *
1659 * @return the cloned root node
1660 */
1661 public ConfigurationNode getClone()
1662 {
1663 return result;
1664 }
1665 }
1666
1667 /**
1668 * A specialized visitor base class that can be used for storing the tree of
1669 * configuration nodes. The basic idea is that each node can be associated
1670 * with a reference object. This reference object has a concrete meaning in
1671 * a derived class, e.g. an entry in a JNDI context or an XML element. When
1672 * the configuration tree is set up, the {@code load()} method is
1673 * responsible for setting the reference objects. When the configuration
1674 * tree is later modified, new nodes do not have a defined reference object.
1675 * This visitor class processes all nodes and finds the ones without a
1676 * defined reference object. For those nodes the {@code insert()}
1677 * method is called, which must be defined in concrete sub classes. This
1678 * method can perform all steps to integrate the new node into the original
1679 * structure.
1680 *
1681 */
1682 protected abstract static class BuilderVisitor extends NodeVisitor
1683 {
1684 /**
1685 * Visits the specified node before its children have been traversed.
1686 *
1687 * @param node the node to visit
1688 * @param key the current key
1689 */
1690 @Override
1691 public void visitBeforeChildren(Node node, ConfigurationKey key)
1692 {
1693 Collection<ConfigurationNode> subNodes = new LinkedList<ConfigurationNode>(node.getChildren());
1694 subNodes.addAll(node.getAttributes());
1695 Iterator<ConfigurationNode> children = subNodes.iterator();
1696 Node sibling1 = null;
1697 Node nd = null;
1698
1699 while (children.hasNext())
1700 {
1701 // find the next new node
1702 do
1703 {
1704 sibling1 = nd;
1705 Object obj = children.next();
1706 nd = getNodeFor(obj);
1707 } while (nd.getReference() != null && children.hasNext());
1708
1709 if (nd.getReference() == null)
1710 {
1711 // find all following new nodes
1712 List<Node> newNodes = new LinkedList<Node>();
1713 newNodes.add(nd);
1714 while (children.hasNext())
1715 {
1716 Object obj = children.next();
1717 nd = getNodeFor(obj);
1718 if (nd.getReference() == null)
1719 {
1720 newNodes.add(nd);
1721 }
1722 else
1723 {
1724 break;
1725 }
1726 }
1727
1728 // Insert all new nodes
1729 Node sibling2 = (nd.getReference() == null) ? null : nd;
1730 for (Node insertNode : newNodes)
1731 {
1732 if (insertNode.getReference() == null)
1733 {
1734 Object ref = insert(insertNode, node, sibling1, sibling2);
1735 if (ref != null)
1736 {
1737 insertNode.setReference(ref);
1738 }
1739 sibling1 = insertNode;
1740 }
1741 }
1742 }
1743 }
1744 }
1745
1746 /**
1747 * Inserts a new node into the structure constructed by this builder.
1748 * This method is called for each node that has been added to the
1749 * configuration tree after the configuration has been loaded from its
1750 * source. These new nodes have to be inserted into the original
1751 * structure. The passed in nodes define the position of the node to be
1752 * inserted: its parent and the siblings between to insert. The return
1753 * value is interpreted as the new reference of the affected
1754 * {@code Node} object; if it is not <b>null </b>, it is passed
1755 * to the node's {@code setReference()} method.
1756 *
1757 * @param newNode the node to be inserted
1758 * @param parent the parent node
1759 * @param sibling1 the sibling after which the node is to be inserted;
1760 * can be <b>null </b> if the new node is going to be the first child
1761 * node
1762 * @param sibling2 the sibling before which the node is to be inserted;
1763 * can be <b>null </b> if the new node is going to be the last child
1764 * node
1765 * @return the reference object for the node to be inserted
1766 */
1767 protected abstract Object insert(Node newNode, Node parent, Node sibling1, Node sibling2);
1768 }
1769 }