    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
    018package org.apache.commons.configuration2;
    019
    020import java.util.ArrayList;
    021import java.util.Collection;
    022import java.util.Collections;
    023import java.util.HashMap;
    024import java.util.Iterator;
    025import java.util.LinkedHashSet;
    026import java.util.LinkedList;
    027import java.util.List;
    028import java.util.Map;
    029import java.util.Set;
    030import java.util.Stack;
    031
    032import org.apache.commons.configuration2.event.ConfigurationEvent;
    033import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
    034import org.apache.commons.configuration2.sync.NoOpSynchronizer;
    035import org.apache.commons.configuration2.tree.ConfigurationNodeVisitorAdapter;
    036import org.apache.commons.configuration2.tree.DefaultExpressionEngine;
    037import org.apache.commons.configuration2.tree.ExpressionEngine;
    038import org.apache.commons.configuration2.tree.NodeAddData;
    039import org.apache.commons.configuration2.tree.NodeHandler;
    040import org.apache.commons.configuration2.tree.NodeKeyResolver;
    041import org.apache.commons.configuration2.tree.NodeModel;
    042import org.apache.commons.configuration2.tree.NodeTreeWalker;
    043import org.apache.commons.configuration2.tree.NodeUpdateData;
    044import org.apache.commons.configuration2.tree.QueryResult;
    045
    046/**
    047 * <p>
    048 * A specialized configuration class that extends its base class by the ability
    049 * of keeping more structure in the stored properties.
    050 * </p>
    051 * <p>
    052 * There are some sources of configuration data that cannot be stored very well
    053 * in a {@code BaseConfiguration} object because then their structure is lost.
    054 * This is for instance true for XML documents. This class can deal with such
    055 * structured configuration sources by storing the properties in a tree-like
    056 * organization. The exact storage structure of the underlying data does not
    057 * matter for the configuration instance; it uses a {@link NodeModel} object for
    058 * accessing it.
    059 * </p>
    060 * <p>
    061 * The hierarchical organization allows for a more sophisticated access to
    062 * single properties. As an example consider the following XML document:
    063 * </p>
    064 *
    065 * <pre>
    066 * &lt;database&gt;
    067 *   &lt;tables&gt;
    068 *     &lt;table&gt;
    069 *       &lt;name&gt;users&lt;/name&gt;
    070 *       &lt;fields&gt;
    071 *         &lt;field&gt;
    072 *           &lt;name&gt;lid&lt;/name&gt;
    073 *           &lt;type&gt;long&lt;/name&gt;
    074 *         &lt;/field&gt;
    075 *         &lt;field&gt;
    076 *           &lt;name&gt;usrName&lt;/name&gt;
    077 *           &lt;type&gt;java.lang.String&lt;/type&gt;
    078 *         &lt;/field&gt;
    079 *        ...
    080 *       &lt;/fields&gt;
    081 *     &lt;/table&gt;
    082 *     &lt;table&gt;
    083 *       &lt;name&gt;documents&lt;/name&gt;
    084 *       &lt;fields&gt;
    085 *         &lt;field&gt;
    086 *           &lt;name&gt;docid&lt;/name&gt;
    087 *           &lt;type&gt;long&lt;/type&gt;
    088 *         &lt;/field&gt;
    089 *         ...
    090 *       &lt;/fields&gt;
    091 *     &lt;/table&gt;
    092 *     ...
    093 *   &lt;/tables&gt;
    094 * &lt;/database&gt;
    095 * </pre>
    096 *
    097 * <p>
    098 * If this document is parsed and stored in a hierarchical configuration object
    099 * (which can be done by one of the sub classes), there are enhanced
    100 * possibilities of accessing properties. Per default, the keys for querying
    101 * information can contain indices that select a specific element if there are
    102 * multiple hits.
    103 * </p>
    104 * <p>
    105 * For instance the key {@code tables.table(0).name} can be used to find out the
    106 * name of the first table. In opposite {@code tables.table.name} would return a
    107 * collection with the names of all available tables. Similarly the key
    108 * {@code tables.table(1).fields.field.name} returns a collection with the names
    109 * of all fields of the second table. If another index is added after the
    110 * {@code field} element, a single field can be accessed:
    111 * {@code tables.table(1).fields.field(0).name}.
    112 * </p>
    113 * <p>
    114 * There is a {@code getMaxIndex()} method that returns the maximum allowed
    115 * index that can be added to a given property key. This method can be used to
    116 * iterate over all values defined for a certain property.
    117 * </p>
    118 * <p>
    119 * Since the 1.3 release of <em>Commons Configuration</em> hierarchical
    120 * configurations support an <em>expression engine</em>. This expression engine
    121 * is responsible for evaluating the passed in configuration keys and map them
    122 * to the stored properties. The examples above are valid for the default
    123 * expression engine, which is used when a new
    124 * {@code AbstractHierarchicalConfiguration} instance is created. With the
    125 * {@code setExpressionEngine()} method a different expression engine can be
    126 * set. For instance with
    127 * {@link org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine}
    128 * there is an expression engine available that supports configuration keys in
    129 * XPATH syntax.
    130 * </p>
    131 * <p>
    132 * In addition to the events common for all configuration classes, hierarchical
    133 * configurations support some more events that correspond to some specific
    134 * methods and features. For those events specific event type constants in
    135 * {@code ConfigurationEvent} exist:
    136 * </p>
    137 * <dl>
    138 * <dt><em>ADD_NODES</em></dt>
    139 * <dd>The {@code addNodes()} method was called; the event object contains the
    140 * key, to which the nodes were added, and a collection with the new nodes as
    141 * value.</dd>
    142 * <dt><em>CLEAR_TREE</em></dt>
    143 * <dd>The {@code clearTree()} method was called; the event object stores the
    144 * key of the removed sub tree.</dd>
    145 * <dt><em>SUBNODE_CHANGED</em></dt>
    146 * <dd>A {@code SubnodeConfiguration} that was created from this configuration
    147 * has been changed. The value property of the event object contains the
    148 * original event object as it was sent by the subnode configuration.</dd>
    149 * </dl>
    150 * <p>
    151 * Whether an {@code AbstractHierarchicalConfiguration} object is thread-safe or
    152 * not depends on the underlying {@code NodeModel} and the
    153 * {@link org.apache.commons.configuration2.sync.Synchronizer Synchronizer}
    154 * it is associated with. Some {@code NodeModel} implementations are inherently
    155 * thread-safe; they do not require a special {@code Synchronizer}. (Per
    156 * default, a dummy {@code Synchronizer} is used which is not thread-safe!) The
    157 * methods for querying or updating configuration data invoke this
    158 * {@code Synchronizer} accordingly. When accessing the configuration's root
    159 * node directly, the client application is responsible for proper
    160 * synchronization. This is achieved by calling the methods
    161 * {@link #lock(org.apache.commons.configuration2.sync.LockMode) lock()},
    162 * and {@link #unlock(org.apache.commons.configuration2.sync.LockMode) unlock()} with a proper
    163 * {@link org.apache.commons.configuration2.sync.LockMode LockMode} argument.
    164 * In any case, it is recommended to not access the
    165 * root node directly, but to use corresponding methods for querying or updating
    166 * configuration data instead. Direct manipulations of a configuration's node
    167 * structure circumvent many internal mechanisms and thus can cause undesired
    168 * effects. For concrete subclasses dealing with specific node structures, this
    169 * situation may be different.
    170 * </p>
    171 *
    172 * @since 2.0
    173 * @param <T> the type of the nodes managed by this hierarchical configuration
    174 */
    175public abstract class AbstractHierarchicalConfiguration<T> extends AbstractConfiguration
    176    implements Cloneable, NodeKeyResolver<T>, HierarchicalConfiguration<T>
    177{
    178    /** The model for managing the data stored in this configuration. */
    179    private NodeModel<T> model;
    180
    181    /** Stores the expression engine for this instance.*/
    182    private ExpressionEngine expressionEngine;
    183
    184    /**
    185     * Creates a new instance of {@code AbstractHierarchicalConfiguration} and
    186     * sets the {@code NodeModel} to be used.
    187     *
    188     * @param nodeModel the {@code NodeModel}
    189     */
    190    protected AbstractHierarchicalConfiguration(final NodeModel<T> nodeModel)
    191    {
    192        model = nodeModel;
    193    }
    194
    195    /**
    196     * {@inheritDoc} This implementation handles synchronization and delegates
    197     * to {@code getRootElementNameInternal()}.
    198     */
    199    @Override
    200    public final String getRootElementName()
    201    {
    202        beginRead(false);
    203        try
    204        {
    205            return getRootElementNameInternal();
    206        }
    207        finally
    208        {
    209            endRead();
    210        }
    211    }
    212
    213    /**
    214     * Actually obtains the name of the root element. This method is called by
    215     * {@code getRootElementName()}. It just returns the name of the root node.
    216     * Subclasses that treat the root element name differently can override this
    217     * method.
    218     *
    219     * @return the name of this configuration's root element
    220     */
    221    protected String getRootElementNameInternal()
    222    {
    223        final NodeHandler<T> nodeHandler = getModel().getNodeHandler();
    224        return nodeHandler.nodeName(nodeHandler.getRootNode());
    225    }
    226
    227    /**
    228     * {@inheritDoc} This implementation returns the configuration's
    229     * {@code NodeModel}. It is guarded by the current {@code Synchronizer}.
    230     */
    231    @Override
    232    public NodeModel<T> getNodeModel()
    233    {
    234        beginRead(false);
    235        try
    236        {
    237            return getModel();
    238        }
    239        finally
    240        {
    241            endRead();
    242        }
    243    }
    244
    245    /**
    246     * Returns the expression engine used by this configuration. This method
    247     * will never return <b>null</b>; if no specific expression engine was set,
    248     * the default expression engine will be returned.
    249     *
    250     * @return the current expression engine
    251     * @since 1.3
    252     */
    253    @Override
    254    public ExpressionEngine getExpressionEngine()
    255    {
    256        return expressionEngine != null ? expressionEngine
    257                : DefaultExpressionEngine.INSTANCE;
    258    }
    259
    260    /**
    261     * Sets the expression engine to be used by this configuration. All property
    262     * keys this configuration has to deal with will be interpreted by this
    263     * engine.
    264     *
    265     * @param expressionEngine the new expression engine; can be <b>null</b>,
    266     * then the default expression engine will be used
    267     * @since 1.3
    268     */
    269    @Override
    270    public void setExpressionEngine(final ExpressionEngine expressionEngine)
    271    {
    272        this.expressionEngine = expressionEngine;
    273    }
    274
    275    /**
    276     * Fetches the specified property. This task is delegated to the associated
    277     * expression engine.
    278     *
    279     * @param key the key to be looked up
    280     * @return the found value
    281     */
    282    @Override
    283    protected Object getPropertyInternal(final String key)
    284    {
    285        final List<QueryResult<T>> results = fetchNodeList(key);
    286
    287        if (results.isEmpty())
    288        {
    289            return null;
    290        }
    291        final NodeHandler<T> handler = getModel().getNodeHandler();
    292        final List<Object> list = new ArrayList<>();
    293        for (final QueryResult<T> result : results)
    294        {
    295            final Object value = valueFromResult(result, handler);
    296            if (value != null)
    297            {
    298                list.add(value);
    299            }
    300        }
    301
    302        if (list.size() < 1)
    303        {
    304            return null;
    305        }
    306        return list.size() == 1 ? list.get(0) : list;
    307    }
    308
    309    /**
    310     * Adds the property with the specified key. This task will be delegated to
    311     * the associated {@code ExpressionEngine}, so the passed in key
    312     * must match the requirements of this implementation.
    313     *
    314     * @param key the key of the new property
    315     * @param obj the value of the new property
    316     */
    317    @Override
    318    protected void addPropertyInternal(final String key, final Object obj)
    319    {
    320        addPropertyToModel(key, getListDelimiterHandler().parse(obj));
    321    }
    322
    323    /**
    324     * {@inheritDoc} This method is not called in the normal way (via
    325     * {@code addProperty()} for hierarchical configurations because all values
    326     * to be added for the property have to be passed to the model in a single
    327     * step. However, to allow derived classes to add an arbitrary value as an
    328     * object, a special implementation is provided here. The passed in object
    329     * is not parsed as a list, but passed directly as only value to the model.
    330     */
    331    @Override
    332    protected void addPropertyDirect(final String key, final Object value)
    333    {
    334        addPropertyToModel(key, Collections.singleton(value));
    335    }
    336
    337    /**
    338     * Helper method for executing an add property operation on the model.
    339     *
    340     * @param key the key of the new property
    341     * @param values the values to be added for this property
    342     */
    343    private void addPropertyToModel(final String key, final Iterable<?> values)
    344    {
    345        getModel().addProperty(key, values, this);
    346    }
    347
    348    /**
    349     * Adds a collection of nodes at the specified position of the configuration
    350     * tree. This method works similar to {@code addProperty()}, but
    351     * instead of a single property a whole collection of nodes can be added -
    352     * and thus complete configuration sub trees. E.g. with this method it is
    353     * possible to add parts of another {@code BaseHierarchicalConfiguration}
    354     * object to this object. If the passed in key refers to
    355     * an existing and unique node, the new nodes are added to this node.
    356     * Otherwise a new node will be created at the specified position in the
    357     * hierarchy. Implementation node: This method performs some book-keeping
    358     * and then delegates to {@code addNodesInternal()}.
    359     *
    360     * @param key the key where the nodes are to be added; can be <b>null</b>,
    361     * then they are added to the root node
    362     * @param nodes a collection with the {@code Node} objects to be
    363     * added
    364     */
    365    @Override
    366    public final void addNodes(final String key, final Collection<? extends T> nodes)
    367    {
    368        if (nodes == null || nodes.isEmpty())
    369        {
    370            return;
    371        }
    372
    373        beginWrite(false);
    374        try
    375        {
    376            fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, true);
    377            addNodesInternal(key, nodes);
    378            fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, false);
    379        }
    380        finally
    381        {
    382            endWrite();
    383        }
    384    }
    385
    386    /**
    387     * Actually adds a collection of new nodes to this configuration. This
    388     * method is called by {@code addNodes()}. It can be overridden by
    389     * subclasses that need to adapt this operation.
    390     *
    391     * @param key the key where the nodes are to be added; can be <b>null</b>,
    392     *        then they are added to the root node
    393     * @param nodes a collection with the {@code Node} objects to be added
    394     * @since 2.0
    395     */
    396    protected void addNodesInternal(final String key, final Collection<? extends T> nodes)
    397    {
    398        getModel().addNodes(key, nodes, this);
    399    }
    400
    401    /**
    402     * Checks if this configuration is empty. Empty means that there are no keys
    403     * with any values, though there can be some (empty) nodes.
    404     *
    405     * @return a flag if this configuration is empty
    406     */
    407    @Override
    408    protected boolean isEmptyInternal()
    409    {
    410        return !nodeDefined(getModel().getNodeHandler().getRootNode());
    411    }
    412
    413    /**
    414     * Checks if the specified key is contained in this configuration. Note that
    415     * for this configuration the term &quot;contained&quot; means that the key
    416     * has an associated value. If there is a node for this key that has no
    417     * value but children (either defined or undefined), this method will still
    418     * return <b>false </b>.
    419     *
    420     * @param key the key to be checked
    421     * @return a flag if this key is contained in this configuration
    422     */
    423    @Override
    424    protected boolean containsKeyInternal(final String key)
    425    {
    426        return getPropertyInternal(key) != null;
    427    }
    428
    429    /**
    430     * Sets the value of the specified property.
    431     *
    432     * @param key the key of the property to set
    433     * @param value the new value of this property
    434     */
    435    @Override
    436    protected void setPropertyInternal(final String key, final Object value)
    437    {
    438        getModel().setProperty(key, value, this);
    439    }
    440
    441    /**
    442     * {@inheritDoc} This implementation delegates to the expression engine.
    443     */
    444    @Override
    445    public List<QueryResult<T>> resolveKey(final T root, final String key,
    446            final NodeHandler<T> handler)
    447    {
    448        return getExpressionEngine().query(root, key, handler);
    449    }
    450
    451    /**
    452     * {@inheritDoc} This implementation delegates to {@code resolveKey()} and
    453     * then filters out attribute results.
    454     */
    455    @Override
    456    public List<T> resolveNodeKey(final T root, final String key, final NodeHandler<T> handler)
    457    {
    458        final List<QueryResult<T>> results = resolveKey(root, key, handler);
    459        final List<T> targetNodes = new LinkedList<>();
    460        for (final QueryResult<T> result : results)
    461        {
    462            if (!result.isAttributeResult())
    463            {
    464                targetNodes.add(result.getNode());
    465            }
    466        }
    467        return targetNodes;
    468    }
    469
    470    /**
    471     * {@inheritDoc} This implementation delegates to the expression engine.
    472     */
    473    @Override
    474    public NodeAddData<T> resolveAddKey(final T root, final String key,
    475            final NodeHandler<T> handler)
    476    {
    477        return getExpressionEngine().prepareAdd(root, key, handler);
    478    }
    479
    480    /**
    481     * {@inheritDoc} This implementation executes a query for the given key and
    482     * constructs a {@code NodeUpdateData} object based on the results. It
    483     * determines which nodes need to be changed and whether new ones need to be
    484     * added or existing ones need to be removed.
    485     */
    486    @Override
    487    public NodeUpdateData<T> resolveUpdateKey(final T root, final String key,
    488            final Object newValue, final NodeHandler<T> handler)
    489    {
    490        final Iterator<QueryResult<T>> itNodes = fetchNodeList(key).iterator();
    491        final Iterator<?> itValues = getListDelimiterHandler().parse(newValue).iterator();
    492        final Map<QueryResult<T>, Object> changedValues =
    493                new HashMap<>();
    494        Collection<Object> additionalValues = null;
    495        Collection<QueryResult<T>> removedItems = null;
    496
    497        while (itNodes.hasNext() && itValues.hasNext())
    498        {
    499            changedValues.put(itNodes.next(), itValues.next());
    500        }
    501
    502        // Add additional nodes if necessary
    503        if (itValues.hasNext())
    504        {
    505            additionalValues = new LinkedList<>();
    506            while (itValues.hasNext())
    507            {
    508                additionalValues.add(itValues.next());
    509            }
    510        }
    511
    512        // Remove remaining nodes
    513        if (itNodes.hasNext())
    514        {
    515            removedItems = new LinkedList<>();
    516            while (itNodes.hasNext())
    517            {
    518                removedItems.add(itNodes.next());
    519            }
    520        }
    521
    522        return new NodeUpdateData<>(changedValues, additionalValues,
    523                removedItems, key);
    524    }
    525
    526    /**
    527     * {@inheritDoc} This implementation uses the expression engine to generate a
    528     * canonical key for the passed in node. For this purpose, the path to the
    529     * root node has to be traversed. The cache is used to store and access keys
    530     * for nodes encountered on the path.
    531     */
    532    @Override
    533    public String nodeKey(final T node, final Map<T, String> cache, final NodeHandler<T> handler)
    534    {
    535        final List<T> path = new LinkedList<>();
    536        T currentNode = node;
    537        String key = cache.get(node);
    538        while (key == null && currentNode != null)
    539        {
    540            path.add(0, currentNode);
    541            currentNode = handler.getParent(currentNode);
    542            key = cache.get(currentNode);
    543        }
    544
    545        for (final T n : path)
    546        {
    547            final String currentKey = getExpressionEngine().canonicalKey(n, key, handler);
    548            cache.put(n, currentKey);
    549            key = currentKey;
    550        }
    551
    552        return key;
    553    }
    554
    555    /**
    556     * Clears this configuration. This is a more efficient implementation than
    557     * the one inherited from the base class. It delegates to the node model.
    558     */
    559    @Override
    560    protected void clearInternal()
    561    {
    562        getModel().clear(this);
    563    }
    564
    565    /**
    566     * Removes all values of the property with the given name and of keys that
    567     * start with this name. So if there is a property with the key
    568     * &quot;foo&quot; and a property with the key &quot;foo.bar&quot;, a call
    569     * of {@code clearTree("foo")} would remove both properties.
    570     *
    571     * @param key the key of the property to be removed
    572     */
    573    @Override
    574    public final void clearTree(final String key)
    575    {
    576        beginWrite(false);
    577        try
    578        {
    579            fireEvent(ConfigurationEvent.CLEAR_TREE, key, null, true);
    580            final Object nodes = clearTreeInternal(key);
    581            fireEvent(ConfigurationEvent.CLEAR_TREE, key, nodes, false);
    582        }
    583        finally
    584        {
    585            endWrite();
    586        }
    587    }
    588
    589    /**
    590     * Actually clears the tree of elements referenced by the given key. This
    591     * method is called by {@code clearTree()}. Subclasses that need to adapt
    592     * this operation can override this method. This base implementation
    593     * delegates to the node model.
    594     *
    595     * @param key the key of the property to be removed
    596     * @return an object with information about the nodes that have been removed
    597     *         (this is needed for firing a meaningful event of type
    598     *         CLEAR_TREE)
    599     * @since 2.0
    600     */
    601    protected Object clearTreeInternal(final String key)
    602    {
    603        return getModel().clearTree(key, this);
    604    }
    605
    606    /**
    607     * Removes the property with the given key. Properties with names that start
    608     * with the given key (i.e. properties below the specified key in the
    609     * hierarchy) won't be affected. This implementation delegates to the node+
    610     * model.
    611     *
    612     * @param key the key of the property to be removed
    613     */
    614    @Override
    615    protected void clearPropertyDirect(final String key)
    616    {
    617        getModel().clearProperty(key, this);
    618    }
    619
    620    /**
    621     * {@inheritDoc} This implementation is slightly more efficient than the
    622     * default implementation. It does not iterate over the key set, but
    623     * directly queries its size after it has been constructed. Note that
    624     * constructing the key set is still an O(n) operation.
    625     */
    626    @Override
    627    protected int sizeInternal()
    628    {
    629        return visitDefinedKeys().getKeyList().size();
    630    }
    631
    632    /**
    633     * Returns an iterator with all keys defined in this configuration.
    634     * Note that the keys returned by this method will not contain any
    635     * indices. This means that some structure will be lost.
    636     *
    637     * @return an iterator with the defined keys in this configuration
    638     */
    639    @Override
    640    protected Iterator<String> getKeysInternal()
    641    {
    642        return visitDefinedKeys().getKeyList().iterator();
    643    }
    644
    645    /**
    646     * Creates a {@code DefinedKeysVisitor} and visits all defined keys with it.
    647     *
    648     * @return the visitor after all keys have been visited
    649     */
    650    private DefinedKeysVisitor visitDefinedKeys()
    651    {
    652        final DefinedKeysVisitor visitor = new DefinedKeysVisitor();
    653        final NodeHandler<T> nodeHandler = getModel().getNodeHandler();
    654        NodeTreeWalker.INSTANCE.walkDFS(nodeHandler.getRootNode(), visitor,
    655                nodeHandler);
    656        return visitor;
    657    }
    658
    659    /**
    660     * Returns an iterator with all keys defined in this configuration that
    661     * start with the given prefix. The returned keys will not contain any
    662     * indices. This implementation tries to locate a node whose key is the same
    663     * as the passed in prefix. Then the subtree of this node is traversed, and
    664     * the keys of all nodes encountered (including attributes) are added to the
    665     * result set.
    666     *
    667     * @param prefix the prefix of the keys to start with
    668     * @return an iterator with the found keys
    669     */
    670    @Override
    671    protected Iterator<String> getKeysInternal(final String prefix)
    672    {
    673        final DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
    674        if (containsKey(prefix))
    675        {
    676            // explicitly add the prefix
    677            visitor.getKeyList().add(prefix);
    678        }
    679
    680        final List<QueryResult<T>> results = fetchNodeList(prefix);
    681        final NodeHandler<T> handler = getModel().getNodeHandler();
    682
    683        for (final QueryResult<T> result : results)
    684        {
    685            if (!result.isAttributeResult())
    686            {
    687                for (final T c : handler.getChildren(result.getNode()))
    688                {
    689                    NodeTreeWalker.INSTANCE.walkDFS(c, visitor, handler);
    690                }
    691                visitor.handleAttributeKeys(prefix, result.getNode(), handler);
    692            }
    693        }
    694
    695        return visitor.getKeyList().iterator();
    696    }
    697
    698    /**
    699     * Returns the maximum defined index for the given key. This is useful if
    700     * there are multiple values for this key. They can then be addressed
    701     * separately by specifying indices from 0 to the return value of this
    702     * method. If the passed in key is not contained in this configuration,
    703     * result is -1.
    704     *
    705     * @param key the key to be checked
    706     * @return the maximum defined index for this key
    707     */
    708    @Override
    709    public final int getMaxIndex(final String key)
    710    {
    711        beginRead(false);
    712        try
    713        {
    714            return getMaxIndexInternal(key);
    715        }
    716        finally
    717        {
    718            endRead();
    719        }
    720    }
    721
    722    /**
    723     * Actually retrieves the maximum defined index for the given key. This
    724     * method is called by {@code getMaxIndex()}. Subclasses that need to adapt
    725     * this operation have to override this method.
    726     *
    727     * @param key the key to be checked
    728     * @return the maximum defined index for this key
    729     * @since 2.0
    730     */
    731    protected int getMaxIndexInternal(final String key)
    732    {
    733        return fetchNodeList(key).size() - 1;
    734    }
    735
    736    /**
    737     * Creates a copy of this object. This new configuration object will contain
    738     * copies of all nodes in the same structure. Registered event listeners
    739     * won't be cloned; so they are not registered at the returned copy.
    740     *
    741     * @return the copy
    742     * @since 1.2
    743     */
    744    @Override
    745    public Object clone()
    746    {
    747        beginRead(false);
    748        try
    749        {
    750            @SuppressWarnings("unchecked") // clone returns the same type
    751            final
    752            AbstractHierarchicalConfiguration<T> copy =
    753                    (AbstractHierarchicalConfiguration<T>) super.clone();
    754            copy.setSynchronizer(NoOpSynchronizer.INSTANCE);
    755            copy.cloneInterpolator(this);
    756            copy.setSynchronizer(ConfigurationUtils.cloneSynchronizer(getSynchronizer()));
    757            copy.model = cloneNodeModel();
    758
    759            return copy;
    760        }
    761        catch (final CloneNotSupportedException cex)
    762        {
    763            // should not happen
    764            throw new ConfigurationRuntimeException(cex);
    765        }
    766        finally
    767        {
    768            endRead();
    769        }
    770    }
    771
    772    /**
    773     * Creates a clone of the node model. This method is called by
    774     * {@code clone()}.
    775     *
    776     * @return the clone of the {@code NodeModel}
    777     * @since 2.0
    778     */
    779    protected abstract NodeModel<T> cloneNodeModel();
    780
    781    /**
    782     * Helper method for resolving the specified key.
    783     *
    784     * @param key the key
    785     * @return a list with all results selected by this key
    786     */
    787    protected List<QueryResult<T>> fetchNodeList(final String key)
    788    {
    789        final NodeHandler<T> nodeHandler = getModel().getNodeHandler();
    790        return resolveKey(nodeHandler.getRootNode(), key, nodeHandler);
    791    }
    792
    793    /**
    794     * Checks if the specified node is defined.
    795     *
    796     * @param node the node to be checked
    797     * @return a flag if this node is defined
    798     */
    799    protected boolean nodeDefined(final T node)
    800    {
    801        final DefinedVisitor<T> visitor = new DefinedVisitor<>();
    802        NodeTreeWalker.INSTANCE.walkBFS(node, visitor, getModel().getNodeHandler());
    803        return visitor.isDefined();
    804    }
    805
    806    /**
    807     * Returns the {@code NodeModel} used by this configuration. This method is
    808     * intended for internal use only. Access to the model is granted without
    809     * any synchronization. This is in contrast to the &quot;official&quot;
    810     * {@code getNodeModel()} method which is guarded by the configuration's
    811     * {@code Synchronizer}.
    812     *
    813     * @return the node model
    814     */
    815    protected NodeModel<T> getModel()
    816    {
    817        return model;
    818    }
    819
    820    /**
    821     * Extracts the value from a query result.
    822     *
    823     * @param result the {@code QueryResult}
    824     * @param handler the {@code NodeHandler}
    825     * @return the value of this result (may be <b>null</b>)
    826     */
    827    private Object valueFromResult(final QueryResult<T> result, final NodeHandler<T> handler)
    828    {
    829        return result.isAttributeResult() ? result.getAttributeValue(handler)
    830                : handler.getValue(result.getNode());
    831    }
    832
    833    /**
    834     * A specialized visitor that checks if a node is defined.
    835     * &quot;Defined&quot; in this terms means that the node or at least one of
    836     * its sub nodes is associated with a value.
    837     *
    838     * @param <T> the type of the nodes managed by this hierarchical configuration
    839     */
    840    private static class DefinedVisitor<T> extends
    841            ConfigurationNodeVisitorAdapter<T>
    842    {
    843        /** Stores the defined flag. */
    844        private boolean defined;
    845
    846        /**
    847         * Checks if iteration should be stopped. This can be done if the first
    848         * defined node is found.
    849         *
    850         * @return a flag if iteration should be stopped
    851         */
    852        @Override
    853        public boolean terminate()
    854        {
    855            return isDefined();
    856        }
    857
    858        /**
    859         * Visits the node. Checks if a value is defined.
    860         *
    861         * @param node the actual node
    862         */
    863        @Override
    864        public void visitBeforeChildren(final T node, final NodeHandler<T> handler)
    865        {
    866            defined =
    867                    handler.getValue(node) != null
    868                            || !handler.getAttributes(node).isEmpty();
    869        }
    870
    871        /**
    872         * Returns the defined flag.
    873         *
    874         * @return the defined flag
    875         */
    876        public boolean isDefined()
    877        {
    878            return defined;
    879        }
    880    }
    881
    882    /**
    883     * A specialized visitor that fills a list with keys that are defined in a
    884     * node hierarchy.
    885     */
    886    private class DefinedKeysVisitor extends
    887            ConfigurationNodeVisitorAdapter<T>
    888    {
    889        /** Stores the list to be filled. */
    890        private final Set<String> keyList;
    891
    892        /** A stack with the keys of the already processed nodes. */
    893        private final Stack<String> parentKeys;
    894
    895        /**
    896         * Default constructor.
    897         */
    898        public DefinedKeysVisitor()
    899        {
    900            keyList = new LinkedHashSet<>();
    901            parentKeys = new Stack<>();
    902        }
    903
    904        /**
    905         * Creates a new {@code DefinedKeysVisitor} instance and sets the
    906         * prefix for the keys to fetch.
    907         *
    908         * @param prefix the prefix
    909         */
    910        public DefinedKeysVisitor(final String prefix)
    911        {
    912            this();
    913            parentKeys.push(prefix);
    914        }
    915
    916        /**
    917         * Returns the list with all defined keys.
    918         *
    919         * @return the list with the defined keys
    920         */
    921        public Set<String> getKeyList()
    922        {
    923            return keyList;
    924        }
    925
    926        /**
    927         * {@inheritDoc} This implementation removes this
    928         * node's key from the stack.
    929         */
    930        @Override
    931        public void visitAfterChildren(final T node, final NodeHandler<T> handler)
    932        {
    933            parentKeys.pop();
    934        }
    935
    936        /**
    937         * {@inheritDoc} If this node has a value, its key is added
    938         * to the internal list.
    939         */
    940        @Override
    941        public void visitBeforeChildren(final T node, final NodeHandler<T> handler)
    942        {
    943            final String parentKey = parentKeys.isEmpty() ? null
    944                    : parentKeys.peek();
    945            final String key = getExpressionEngine().nodeKey(node, parentKey, handler);
    946            parentKeys.push(key);
    947            if (handler.getValue(node) != null)
    948            {
    949                keyList.add(key);
    950            }
    951            handleAttributeKeys(key, node, handler);
    952        }
    953
    954        /**
    955         * Appends all attribute keys of the current node.
    956         *
    957         * @param parentKey the parent key
    958         * @param node the current node
    959         * @param handler the {@code NodeHandler}
    960         */
    961        public void handleAttributeKeys(final String parentKey, final T node,
    962                final NodeHandler<T> handler)
    963        {
    964            for (final String attr : handler.getAttributes(node))
    965            {
    966                keyList.add(getExpressionEngine().attributeKey(parentKey, attr));
    967            }
    968        }
    969    }
    970
    971    @Override
    972    public String toString()
    973    {
    974        return super.toString() + "(" + getRootElementNameInternal() + ")";
    975    }
    976}