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