    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     * &lt;database&gt;
    055     *   &lt;tables&gt;
    056     *     &lt;table&gt;
    057     *       &lt;name&gt;users&lt;/name&gt;
    058     *       &lt;fields&gt;
    059     *         &lt;field&gt;
    060     *           &lt;name&gt;lid&lt;/name&gt;
    061     *           &lt;type&gt;long&lt;/name&gt;
    062     *         &lt;/field&gt;
    063     *         &lt;field&gt;
    064     *           &lt;name&gt;usrName&lt;/name&gt;
    065     *           &lt;type&gt;java.lang.String&lt;/type&gt;
    066     *         &lt;/field&gt;
    067     *        ...
    068     *       &lt;/fields&gt;
    069     *     &lt;/table&gt;
    070     *     &lt;table&gt;
    071     *       &lt;name&gt;documents&lt;/name&gt;
    072     *       &lt;fields&gt;
    073     *         &lt;field&gt;
    074     *           &lt;name&gt;docid&lt;/name&gt;
    075     *           &lt;type&gt;long&lt;/type&gt;
    076     *         &lt;/field&gt;
    077     *         ...
    078     *       &lt;/fields&gt;
    079     *     &lt;/table&gt;
    080     *     ...
    081     *   &lt;/tables&gt;
    082     * &lt;/database&gt;
    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 &quot;contained&quot; 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         * &quot;foo&quot; and a property with the key &quot;foo.bar&quot;, 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 &quot;reference&quot;. 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         * &quot;Defined&quot; 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    }