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