AbstractHierarchicalConfiguration.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.configuration2;

  18. import java.util.Collection;
  19. import java.util.Collections;
  20. import java.util.HashMap;
  21. import java.util.Iterator;
  22. import java.util.LinkedHashSet;
  23. import java.util.LinkedList;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Objects;
  27. import java.util.Set;
  28. import java.util.Stack;
  29. import java.util.stream.Collectors;

  30. import org.apache.commons.configuration2.event.ConfigurationEvent;
  31. import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
  32. import org.apache.commons.configuration2.sync.NoOpSynchronizer;
  33. import org.apache.commons.configuration2.tree.ConfigurationNodeVisitorAdapter;
  34. import org.apache.commons.configuration2.tree.DefaultExpressionEngine;
  35. import org.apache.commons.configuration2.tree.ExpressionEngine;
  36. import org.apache.commons.configuration2.tree.NodeAddData;
  37. import org.apache.commons.configuration2.tree.NodeHandler;
  38. import org.apache.commons.configuration2.tree.NodeKeyResolver;
  39. import org.apache.commons.configuration2.tree.NodeModel;
  40. import org.apache.commons.configuration2.tree.NodeTreeWalker;
  41. import org.apache.commons.configuration2.tree.NodeUpdateData;
  42. import org.apache.commons.configuration2.tree.QueryResult;

  43. /**
  44.  * <p>
  45.  * A specialized configuration class that extends its base class by the ability of keeping more structure in the stored
  46.  * properties.
  47.  * </p>
  48.  * <p>
  49.  * There are some sources of configuration data that cannot be stored very well in a {@code BaseConfiguration} object
  50.  * because then their structure is lost. This is for instance true for XML documents. This class can deal with such
  51.  * structured configuration sources by storing the properties in a tree-like organization. The exact storage structure
  52.  * of the underlying data does not matter for the configuration instance; it uses a {@link NodeModel} object for
  53.  * accessing it.
  54.  * </p>
  55.  * <p>
  56.  * The hierarchical organization allows for a more sophisticated access to single properties. As an example consider the
  57.  * following XML document:
  58.  * </p>
  59.  *
  60.  * <pre>
  61.  * &lt;database&gt;
  62.  *   &lt;tables&gt;
  63.  *     &lt;table&gt;
  64.  *       &lt;name&gt;users&lt;/name&gt;
  65.  *       &lt;fields&gt;
  66.  *         &lt;field&gt;
  67.  *           &lt;name&gt;lid&lt;/name&gt;
  68.  *           &lt;type&gt;long&lt;/name&gt;
  69.  *         &lt;/field&gt;
  70.  *         &lt;field&gt;
  71.  *           &lt;name&gt;usrName&lt;/name&gt;
  72.  *           &lt;type&gt;java.lang.String&lt;/type&gt;
  73.  *         &lt;/field&gt;
  74.  *        ...
  75.  *       &lt;/fields&gt;
  76.  *     &lt;/table&gt;
  77.  *     &lt;table&gt;
  78.  *       &lt;name&gt;documents&lt;/name&gt;
  79.  *       &lt;fields&gt;
  80.  *         &lt;field&gt;
  81.  *           &lt;name&gt;docid&lt;/name&gt;
  82.  *           &lt;type&gt;long&lt;/type&gt;
  83.  *         &lt;/field&gt;
  84.  *         ...
  85.  *       &lt;/fields&gt;
  86.  *     &lt;/table&gt;
  87.  *     ...
  88.  *   &lt;/tables&gt;
  89.  * &lt;/database&gt;
  90.  * </pre>
  91.  *
  92.  * <p>
  93.  * If this document is parsed and stored in a hierarchical configuration object (which can be done by one of the sub
  94.  * classes), there are enhanced possibilities of accessing properties. Per default, the keys for querying information
  95.  * can contain indices that select a specific element if there are multiple hits.
  96.  * </p>
  97.  * <p>
  98.  * For instance the key {@code tables.table(0).name} can be used to find out the name of the first table. In opposite
  99.  * {@code tables.table.name} would return a collection with the names of all available tables. Similarly the key
  100.  * {@code tables.table(1).fields.field.name} returns a collection with the names of all fields of the second table. If
  101.  * another index is added after the {@code field} element, a single field can be accessed:
  102.  * {@code tables.table(1).fields.field(0).name}.
  103.  * </p>
  104.  * <p>
  105.  * There is a {@code getMaxIndex()} method that returns the maximum allowed index that can be added to a given property
  106.  * key. This method can be used to iterate over all values defined for a certain property.
  107.  * </p>
  108.  * <p>
  109.  * Since the 1.3 release of <em>Commons Configuration</em> hierarchical configurations support an <em>expression
  110.  * engine</em>. This expression engine is responsible for evaluating the passed in configuration keys and map them to
  111.  * the stored properties. The examples above are valid for the default expression engine, which is used when a new
  112.  * {@code AbstractHierarchicalConfiguration} instance is created. With the {@code setExpressionEngine()} method a
  113.  * different expression engine can be set. For instance with
  114.  * {@link org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine} there is an expression engine available
  115.  * that supports configuration keys in XPATH syntax.
  116.  * </p>
  117.  * <p>
  118.  * In addition to the events common for all configuration classes, hierarchical configurations support some more events
  119.  * that correspond to some specific methods and features. For those events specific event type constants in
  120.  * {@code ConfigurationEvent} exist:
  121.  * </p>
  122.  * <dl>
  123.  * <dt><em>ADD_NODES</em></dt>
  124.  * <dd>The {@code addNodes()} method was called; the event object contains the key, to which the nodes were added, and a
  125.  * collection with the new nodes as value.</dd>
  126.  * <dt><em>CLEAR_TREE</em></dt>
  127.  * <dd>The {@code clearTree()} method was called; the event object stores the key of the removed sub tree.</dd>
  128.  * <dt><em>SUBNODE_CHANGED</em></dt>
  129.  * <dd>A {@code SubnodeConfiguration} that was created from this configuration has been changed. The value property of
  130.  * the event object contains the original event object as it was sent by the subnode configuration.</dd>
  131.  * </dl>
  132.  * <p>
  133.  * Whether an {@code AbstractHierarchicalConfiguration} object is thread-safe or not depends on the underlying
  134.  * {@code NodeModel} and the {@link org.apache.commons.configuration2.sync.Synchronizer Synchronizer} it is associated
  135.  * with. Some {@code NodeModel} implementations are inherently thread-safe; they do not require a special
  136.  * {@code Synchronizer}. (Per default, a dummy {@code Synchronizer} is used which is not thread-safe!) The methods for
  137.  * querying or updating configuration data invoke this {@code Synchronizer} accordingly. When accessing the
  138.  * configuration's root node directly, the client application is responsible for proper synchronization. This is
  139.  * achieved by calling the methods {@link #lock(org.apache.commons.configuration2.sync.LockMode) lock()}, and
  140.  * {@link #unlock(org.apache.commons.configuration2.sync.LockMode) unlock()} with a proper
  141.  * {@link org.apache.commons.configuration2.sync.LockMode LockMode} argument. In any case, it is recommended to not
  142.  * access the root node directly, but to use corresponding methods for querying or updating configuration data instead.
  143.  * Direct manipulations of a configuration's node structure circumvent many internal mechanisms and thus can cause
  144.  * undesired effects. For concrete subclasses dealing with specific node structures, this situation may be different.
  145.  * </p>
  146.  *
  147.  * @param <T> the type of the nodes managed by this hierarchical configuration
  148.  * @since 2.0
  149.  */
  150. public abstract class AbstractHierarchicalConfiguration<T> extends AbstractConfiguration
  151.     implements Cloneable, NodeKeyResolver<T>, HierarchicalConfiguration<T> {

  152.     /**
  153.      * A specialized visitor that fills a list with keys that are defined in a node hierarchy.
  154.      */
  155.     private final class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter<T> {

  156.         /** Stores the list to be filled. */
  157.         private final Set<String> keyList;

  158.         /** A stack with the keys of the already processed nodes. */
  159.         private final Stack<String> parentKeys;

  160.         /**
  161.          * Default constructor.
  162.          */
  163.         public DefinedKeysVisitor() {
  164.             keyList = new LinkedHashSet<>();
  165.             parentKeys = new Stack<>();
  166.         }

  167.         /**
  168.          * Creates a new {@code DefinedKeysVisitor} instance and sets the prefix for the keys to fetch.
  169.          *
  170.          * @param prefix the prefix
  171.          */
  172.         public DefinedKeysVisitor(final String prefix) {
  173.             this();
  174.             parentKeys.push(prefix);
  175.         }

  176.         /**
  177.          * Gets the list with all defined keys.
  178.          *
  179.          * @return the list with the defined keys
  180.          */
  181.         public Set<String> getKeyList() {
  182.             return keyList;
  183.         }

  184.         /**
  185.          * Appends all attribute keys of the current node.
  186.          *
  187.          * @param parentKey the parent key
  188.          * @param node the current node
  189.          * @param handler the {@code NodeHandler}
  190.          */
  191.         public void handleAttributeKeys(final String parentKey, final T node, final NodeHandler<T> handler) {
  192.             handler.getAttributes(node).forEach(attr -> keyList.add(getExpressionEngine().attributeKey(parentKey, attr)));
  193.         }

  194.         /**
  195.          * {@inheritDoc} This implementation removes this node's key from the stack.
  196.          */
  197.         @Override
  198.         public void visitAfterChildren(final T node, final NodeHandler<T> handler) {
  199.             parentKeys.pop();
  200.         }

  201.         /**
  202.          * {@inheritDoc} If this node has a value, its key is added to the internal list.
  203.          */
  204.         @Override
  205.         public void visitBeforeChildren(final T node, final NodeHandler<T> handler) {
  206.             final String parentKey = parentKeys.isEmpty() ? null : parentKeys.peek();
  207.             final String key = getExpressionEngine().nodeKey(node, parentKey, handler);
  208.             parentKeys.push(key);
  209.             if (handler.getValue(node) != null) {
  210.                 keyList.add(key);
  211.             }
  212.             handleAttributeKeys(key, node, handler);
  213.         }
  214.     }

  215.     /**
  216.      * A specialized visitor that checks if a node is defined. &quot;Defined&quot; in this terms means that the node or at
  217.      * least one of its sub nodes is associated with a value.
  218.      *
  219.      * @param <T> the type of the nodes managed by this hierarchical configuration
  220.      */
  221.     private static final class DefinedVisitor<T> extends ConfigurationNodeVisitorAdapter<T> {

  222.         /** Stores the defined flag. */
  223.         private boolean defined;

  224.         /**
  225.          * Returns the defined flag.
  226.          *
  227.          * @return the defined flag
  228.          */
  229.         public boolean isDefined() {
  230.             return defined;
  231.         }

  232.         /**
  233.          * Checks if iteration should be stopped. This can be done if the first defined node is found.
  234.          *
  235.          * @return a flag if iteration should be stopped
  236.          */
  237.         @Override
  238.         public boolean terminate() {
  239.             return isDefined();
  240.         }

  241.         /**
  242.          * Visits the node. Checks if a value is defined.
  243.          *
  244.          * @param node the actual node
  245.          */
  246.         @Override
  247.         public void visitBeforeChildren(final T node, final NodeHandler<T> handler) {
  248.             defined = handler.getValue(node) != null || !handler.getAttributes(node).isEmpty();
  249.         }
  250.     }

  251.     /** The model for managing the data stored in this configuration. */
  252.     private NodeModel<T> nodeModel;

  253.     /** Stores the expression engine for this instance. */
  254.     private ExpressionEngine expressionEngine;

  255.     /**
  256.      * Creates a new instance of {@code AbstractHierarchicalConfiguration} and sets the {@code NodeModel} to be used.
  257.      *
  258.      * @param nodeModel the {@code NodeModel}
  259.      */
  260.     protected AbstractHierarchicalConfiguration(final NodeModel<T> nodeModel) {
  261.         this.nodeModel = nodeModel;
  262.     }

  263.     /**
  264.      * Adds a collection of nodes at the specified position of the configuration tree. This method works similar to
  265.      * {@code addProperty()}, but instead of a single property a whole collection of nodes can be added - and thus complete
  266.      * configuration sub trees. E.g. with this method it is possible to add parts of another
  267.      * {@code BaseHierarchicalConfiguration} object to this object. If the passed in key refers to an existing and unique
  268.      * node, the new nodes are added to this node. Otherwise a new node will be created at the specified position in the
  269.      * hierarchy. Implementation node: This method performs some book-keeping and then delegates to
  270.      * {@code addNodesInternal()}.
  271.      *
  272.      * @param key the key where the nodes are to be added; can be <strong>null</strong>, then they are added to the root node
  273.      * @param nodes a collection with the {@code Node} objects to be added
  274.      */
  275.     @Override
  276.     public final void addNodes(final String key, final Collection<? extends T> nodes) {
  277.         if (nodes == null || nodes.isEmpty()) {
  278.             return;
  279.         }
  280.         syncWrite(() -> {
  281.             fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, true);
  282.             addNodesInternal(key, nodes);
  283.             fireEvent(ConfigurationEvent.ADD_NODES, key, nodes, false);
  284.         }, false);
  285.     }

  286.     /**
  287.      * Actually adds a collection of new nodes to this configuration. This method is called by {@code addNodes()}. It can be
  288.      * overridden by subclasses that need to adapt this operation.
  289.      *
  290.      * @param key the key where the nodes are to be added; can be <strong>null</strong>, then they are added to the root node
  291.      * @param nodes a collection with the {@code Node} objects to be added
  292.      * @since 2.0
  293.      */
  294.     protected void addNodesInternal(final String key, final Collection<? extends T> nodes) {
  295.         getModel().addNodes(key, nodes, this);
  296.     }

  297.     /**
  298.      * {@inheritDoc} This method is not called in the normal way (via {@code addProperty()} for hierarchical configurations
  299.      * because all values to be added for the property have to be passed to the model in a single step. However, to allow
  300.      * derived classes to add an arbitrary value as an object, a special implementation is provided here. The passed in
  301.      * object is not parsed as a list, but passed directly as only value to the model.
  302.      */
  303.     @Override
  304.     protected void addPropertyDirect(final String key, final Object value) {
  305.         addPropertyToModel(key, Collections.singleton(value));
  306.     }

  307.     /**
  308.      * Adds the property with the specified key. This task will be delegated to the associated {@code ExpressionEngine}, so
  309.      * the passed in key must match the requirements of this implementation.
  310.      *
  311.      * @param key the key of the new property
  312.      * @param obj the value of the new property
  313.      */
  314.     @Override
  315.     protected void addPropertyInternal(final String key, final Object obj) {
  316.         addPropertyToModel(key, getListDelimiterHandler().parse(obj));
  317.     }

  318.     /**
  319.      * Helper method for executing an add property operation on the model.
  320.      *
  321.      * @param key the key of the new property
  322.      * @param values the values to be added for this property
  323.      */
  324.     private void addPropertyToModel(final String key, final Iterable<?> values) {
  325.         getModel().addProperty(key, values, this);
  326.     }

  327.     /**
  328.      * Clears this configuration. This is a more efficient implementation than the one inherited from the base class. It
  329.      * delegates to the node model.
  330.      */
  331.     @Override
  332.     protected void clearInternal() {
  333.         getModel().clear(this);
  334.     }

  335.     /**
  336.      * Removes the property with the given key. Properties with names that start with the given key (i.e. properties below
  337.      * the specified key in the hierarchy) won't be affected. This implementation delegates to the node+ model.
  338.      *
  339.      * @param key the key of the property to be removed
  340.      */
  341.     @Override
  342.     protected void clearPropertyDirect(final String key) {
  343.         getModel().clearProperty(key, this);
  344.     }

  345.     /**
  346.      * Removes all values of the property with the given name and of keys that start with this name. So if there is a
  347.      * property with the key &quot;foo&quot; and a property with the key &quot;foo.bar&quot;, a call of
  348.      * {@code clearTree("foo")} would remove both properties.
  349.      *
  350.      * @param key the key of the property to be removed
  351.      */
  352.     @Override
  353.     public final void clearTree(final String key) {
  354.         syncWrite(() -> {
  355.             fireEvent(ConfigurationEvent.CLEAR_TREE, key, null, true);
  356.             fireEvent(ConfigurationEvent.CLEAR_TREE, key, clearTreeInternal(key), false);
  357.         }, false);
  358.     }

  359.     /**
  360.      * Actually clears the tree of elements referenced by the given key. This method is called by {@code clearTree()}.
  361.      * Subclasses that need to adapt this operation can override this method. This base implementation delegates to the node
  362.      * model.
  363.      *
  364.      * @param key the key of the property to be removed
  365.      * @return an object with information about the nodes that have been removed (this is needed for firing a meaningful
  366.      *         event of type CLEAR_TREE)
  367.      * @since 2.0
  368.      */
  369.     protected Object clearTreeInternal(final String key) {
  370.         return getModel().clearTree(key, this);
  371.     }

  372.     /**
  373.      * Creates a copy of this object. This new configuration object will contain copies of all nodes in the same structure.
  374.      * Registered event listeners won't be cloned; so they are not registered at the returned copy.
  375.      *
  376.      * @return the copy
  377.      * @since 1.2
  378.      */
  379.     @SuppressWarnings("unchecked")
  380.     @Override
  381.     public Object clone() {
  382.         return syncRead(() -> {
  383.             try {
  384.                 final AbstractHierarchicalConfiguration<T> copy = (AbstractHierarchicalConfiguration<T>) AbstractHierarchicalConfiguration.super.clone();
  385.                 copy.setSynchronizer(NoOpSynchronizer.INSTANCE);
  386.                 copy.cloneInterpolator(this);
  387.                 copy.setSynchronizer(ConfigurationUtils.cloneSynchronizer(getSynchronizer()));
  388.                 copy.nodeModel = cloneNodeModel();
  389.                 return copy;
  390.             } catch (final CloneNotSupportedException cex) {
  391.                 // should not happen
  392.                 throw new ConfigurationRuntimeException(cex);
  393.             }
  394.         }, false);
  395.     }

  396.     /**
  397.      * Creates a clone of the node model. This method is called by {@code clone()}.
  398.      *
  399.      * @return the clone of the {@code NodeModel}
  400.      * @since 2.0
  401.      */
  402.     protected abstract NodeModel<T> cloneNodeModel();

  403.     /**
  404.      * Checks if the specified key is contained in this configuration. Note that for this configuration the term
  405.      * &quot;contained&quot; means that the key has an associated value. If there is a node for this key that has no value
  406.      * but children (either defined or undefined), this method will still return <strong>false </strong>.
  407.      *
  408.      * @param key the key to be checked
  409.      * @return a flag if this key is contained in this configuration
  410.      */
  411.     @Override
  412.     protected boolean containsKeyInternal(final String key) {
  413.         return getPropertyInternal(key) != null;
  414.     }

  415.     /**
  416.      * Tests whether this configuration contains one or more matches to this value. This operation stops at first
  417.      * match but may be more expensive than the containsKey method.
  418.      * @since 2.11.0
  419.      */
  420.     @Override
  421.     protected boolean containsValueInternal(final Object value) {
  422.         return contains(getKeys(), value);
  423.     }

  424.     /**
  425.      * Helper method for resolving the specified key.
  426.      *
  427.      * @param key the key
  428.      * @return a list with all results selected by this key
  429.      */
  430.     protected List<QueryResult<T>> fetchNodeList(final String key) {
  431.         final NodeHandler<T> nodeHandler = getModel().getNodeHandler();
  432.         return resolveKey(nodeHandler.getRootNode(), key, nodeHandler);
  433.     }

  434.     /**
  435.      * Gets the expression engine used by this configuration. This method will never return <strong>null</strong>; if no specific
  436.      * expression engine was set, the default expression engine will be returned.
  437.      *
  438.      * @return the current expression engine
  439.      * @since 1.3
  440.      */
  441.     @Override
  442.     public ExpressionEngine getExpressionEngine() {
  443.         return expressionEngine != null ? expressionEngine : DefaultExpressionEngine.INSTANCE;
  444.     }

  445.     /**
  446.      * Gets an iterator with all keys defined in this configuration. Note that the keys returned by this method will not
  447.      * contain any indices. This means that some structure will be lost.
  448.      *
  449.      * @return an iterator with the defined keys in this configuration
  450.      */
  451.     @Override
  452.     protected Iterator<String> getKeysInternal() {
  453.         return visitDefinedKeys().getKeyList().iterator();
  454.     }

  455.     /**
  456.      * Gets an iterator with all keys defined in this configuration that start with the given prefix. The returned keys
  457.      * will not contain any indices. This implementation tries to locate a node whose key is the same as the passed in
  458.      * prefix. Then the subtree of this node is traversed, and the keys of all nodes encountered (including attributes) are
  459.      * added to the result set.
  460.      *
  461.      * @param prefix the prefix of the keys to start with
  462.      * @return an iterator with the found keys
  463.      */
  464.     @Override
  465.     protected Iterator<String> getKeysInternal(final String prefix) {
  466.         return getKeysInternal(prefix, DELIMITER);
  467.     }

  468.     /**
  469.      * Gets an iterator with all keys defined in this configuration that start with the given prefix. The returned keys
  470.      * will not contain any indices. This implementation tries to locate a node whose key is the same as the passed in
  471.      * prefix. Then the subtree of this node is traversed, and the keys of all nodes encountered (including attributes) are
  472.      * added to the result set.
  473.      *
  474.      * @param prefix the prefix of the keys to start with
  475.      * @param delimiter TODO
  476.      * @return an iterator with the found keys
  477.      * @since 2.12.0
  478.      */
  479.     @Override
  480.     protected Iterator<String> getKeysInternal(final String prefix, final String delimiter) {
  481.         final DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);
  482.         if (containsKey(prefix)) {
  483.             // explicitly add the prefix
  484.             visitor.getKeyList().add(prefix);
  485.         }

  486.         final List<QueryResult<T>> results = fetchNodeList(prefix);
  487.         final NodeHandler<T> handler = getModel().getNodeHandler();

  488.         results.forEach(result -> {
  489.             if (!result.isAttributeResult()) {
  490.                 handler.getChildren(result.getNode()).forEach(c -> NodeTreeWalker.INSTANCE.walkDFS(c, visitor, handler));
  491.                 visitor.handleAttributeKeys(prefix, result.getNode(), handler);
  492.             }
  493.         });

  494.         return visitor.getKeyList().iterator();
  495.     }
  496.     /**
  497.      * Gets the maximum defined index for the given key. This is useful if there are multiple values for this key. They
  498.      * can then be addressed separately by specifying indices from 0 to the return value of this method. If the passed in
  499.      * key is not contained in this configuration, result is -1.
  500.      *
  501.      * @param key the key to be checked
  502.      * @return the maximum defined index for this key
  503.      */
  504.     @Override
  505.     public final int getMaxIndex(final String key) {
  506.         return syncRead(() -> getMaxIndexInternal(key), false);
  507.     }

  508.     /**
  509.      * Actually retrieves the maximum defined index for the given key. This method is called by {@code getMaxIndex()}.
  510.      * Subclasses that need to adapt this operation have to override this method.
  511.      *
  512.      * @param key the key to be checked
  513.      * @return the maximum defined index for this key
  514.      * @since 2.0
  515.      */
  516.     protected int getMaxIndexInternal(final String key) {
  517.         return fetchNodeList(key).size() - 1;
  518.     }

  519.     /**
  520.      * Gets the {@code NodeModel} used by this configuration. This method is intended for internal use only. Access to
  521.      * the model is granted without any synchronization. This is in contrast to the &quot;official&quot;
  522.      * {@code getNodeModel()} method which is guarded by the configuration's {@code Synchronizer}.
  523.      *
  524.      * @return the node model
  525.      */
  526.     protected NodeModel<T> getModel() {
  527.         return nodeModel;
  528.     }

  529.     /**
  530.      * {@inheritDoc} This implementation returns the configuration's {@code NodeModel}. It is guarded by the current
  531.      * {@code Synchronizer}.
  532.      */
  533.     @Override
  534.     public NodeModel<T> getNodeModel() {
  535.         return syncRead(this::getModel, false);
  536.     }

  537.     /**
  538.      * Fetches the specified property. This task is delegated to the associated expression engine.
  539.      *
  540.      * @param key the key to be looked up
  541.      * @return the found value
  542.      */
  543.     @Override
  544.     protected Object getPropertyInternal(final String key) {
  545.         final List<QueryResult<T>> results = fetchNodeList(key);

  546.         if (results.isEmpty()) {
  547.             return null;
  548.         }
  549.         final NodeHandler<T> handler = getModel().getNodeHandler();
  550.         final List<Object> list = results.stream().map(r -> valueFromResult(r, handler)).filter(Objects::nonNull).collect(Collectors.toList());

  551.         if (list.size() < 1) {
  552.             return null;
  553.         }
  554.         return list.size() == 1 ? list.get(0) : list;
  555.     }

  556.     /**
  557.      * {@inheritDoc} This implementation handles synchronization and delegates to {@code getRootElementNameInternal()}.
  558.      */
  559.     @Override
  560.     public final String getRootElementName() {
  561.         return syncRead(this::getRootElementNameInternal, false);
  562.     }

  563.     /**
  564.      * Actually obtains the name of the root element. This method is called by {@code getRootElementName()}. It just returns
  565.      * the name of the root node. Subclasses that treat the root element name differently can override this method.
  566.      *
  567.      * @return the name of this configuration's root element
  568.      */
  569.     protected String getRootElementNameInternal() {
  570.         final NodeHandler<T> nodeHandler = getModel().getNodeHandler();
  571.         return nodeHandler.nodeName(nodeHandler.getRootNode());
  572.     }

  573.     /**
  574.      * Checks if this configuration is empty. Empty means that there are no keys with any values, though there can be some
  575.      * (empty) nodes.
  576.      *
  577.      * @return a flag if this configuration is empty
  578.      */
  579.     @Override
  580.     protected boolean isEmptyInternal() {
  581.         return !nodeDefined(getModel().getNodeHandler().getRootNode());
  582.     }

  583.     /**
  584.      * Checks if the specified node is defined.
  585.      *
  586.      * @param node the node to be checked
  587.      * @return a flag if this node is defined
  588.      */
  589.     protected boolean nodeDefined(final T node) {
  590.         final DefinedVisitor<T> visitor = new DefinedVisitor<>();
  591.         NodeTreeWalker.INSTANCE.walkBFS(node, visitor, getModel().getNodeHandler());
  592.         return visitor.isDefined();
  593.     }

  594.     /**
  595.      * {@inheritDoc} This implementation uses the expression engine to generate a canonical key for the passed in node. For
  596.      * this purpose, the path to the root node has to be traversed. The cache is used to store and access keys for nodes
  597.      * encountered on the path.
  598.      */
  599.     @Override
  600.     public String nodeKey(final T node, final Map<T, String> cache, final NodeHandler<T> handler) {
  601.         final List<T> paths = new LinkedList<>();
  602.         T currentNode = node;
  603.         String key = cache.get(node);
  604.         while (key == null && currentNode != null) {
  605.             paths.add(0, currentNode);
  606.             currentNode = handler.getParent(currentNode);
  607.             key = cache.get(currentNode);
  608.         }

  609.         for (final T n : paths) {
  610.             final String currentKey = getExpressionEngine().canonicalKey(n, key, handler);
  611.             cache.put(n, currentKey);
  612.             key = currentKey;
  613.         }

  614.         return key;
  615.     }

  616.     /**
  617.      * {@inheritDoc} This implementation delegates to the expression engine.
  618.      */
  619.     @Override
  620.     public NodeAddData<T> resolveAddKey(final T root, final String key, final NodeHandler<T> handler) {
  621.         return getExpressionEngine().prepareAdd(root, key, handler);
  622.     }

  623.     /**
  624.      * {@inheritDoc} This implementation delegates to the expression engine.
  625.      */
  626.     @Override
  627.     public List<QueryResult<T>> resolveKey(final T root, final String key, final NodeHandler<T> handler) {
  628.         return getExpressionEngine().query(root, key, handler);
  629.     }

  630.     /**
  631.      * {@inheritDoc} This implementation delegates to {@code resolveKey()} and then filters out attribute results.
  632.      */
  633.     @Override
  634.     public List<T> resolveNodeKey(final T root, final String key, final NodeHandler<T> handler) {
  635.         return resolveKey(root, key, handler).stream().filter(r -> !r.isAttributeResult()).map(QueryResult::getNode)
  636.             .collect(Collectors.toCollection(LinkedList::new));
  637.     }

  638.     /**
  639.      * {@inheritDoc} This implementation executes a query for the given key and constructs a {@code NodeUpdateData} object
  640.      * based on the results. It determines which nodes need to be changed and whether new ones need to be added or existing
  641.      * ones need to be removed.
  642.      */
  643.     @Override
  644.     public NodeUpdateData<T> resolveUpdateKey(final T root, final String key, final Object newValue, final NodeHandler<T> handler) {
  645.         final Iterator<QueryResult<T>> itNodes = fetchNodeList(key).iterator();
  646.         final Iterator<?> itValues = getListDelimiterHandler().parse(newValue).iterator();
  647.         final Map<QueryResult<T>, Object> changedValues = new HashMap<>();
  648.         Collection<Object> additionalValues = null;
  649.         Collection<QueryResult<T>> removedItems = null;

  650.         while (itNodes.hasNext() && itValues.hasNext()) {
  651.             changedValues.put(itNodes.next(), itValues.next());
  652.         }

  653.         // Add additional nodes if necessary
  654.         if (itValues.hasNext()) {
  655.             additionalValues = new LinkedList<>();
  656.             itValues.forEachRemaining(additionalValues::add);
  657.         }

  658.         // Remove remaining nodes
  659.         if (itNodes.hasNext()) {
  660.             removedItems = new LinkedList<>();
  661.             itNodes.forEachRemaining(removedItems::add);
  662.         }

  663.         return new NodeUpdateData<>(changedValues, additionalValues, removedItems, key);
  664.     }

  665.     /**
  666.      * Sets the expression engine to be used by this configuration. All property keys this configuration has to deal with
  667.      * will be interpreted by this engine.
  668.      *
  669.      * @param expressionEngine the new expression engine; can be <strong>null</strong>, then the default expression engine will be
  670.      *        used
  671.      * @since 1.3
  672.      */
  673.     @Override
  674.     public void setExpressionEngine(final ExpressionEngine expressionEngine) {
  675.         this.expressionEngine = expressionEngine;
  676.     }

  677.     /**
  678.      * Sets the value of the specified property.
  679.      *
  680.      * @param key the key of the property to set
  681.      * @param value the new value of this property
  682.      */
  683.     @Override
  684.     protected void setPropertyInternal(final String key, final Object value) {
  685.         getModel().setProperty(key, value, this);
  686.     }

  687.     /**
  688.      * {@inheritDoc} This implementation is slightly more efficient than the default implementation. It does not iterate
  689.      * over the key set, but directly queries its size after it has been constructed. Note that constructing the key set is
  690.      * still an O(n) operation.
  691.      */
  692.     @Override
  693.     protected int sizeInternal() {
  694.         return visitDefinedKeys().getKeyList().size();
  695.     }

  696.     @Override
  697.     public String toString() {
  698.         return super.toString() + "(" + getRootElementNameInternal() + ")";
  699.     }

  700.     /**
  701.      * Extracts the value from a query result.
  702.      *
  703.      * @param result the {@code QueryResult}
  704.      * @param handler the {@code NodeHandler}
  705.      * @return the value of this result (may be <strong>null</strong>)
  706.      */
  707.     private Object valueFromResult(final QueryResult<T> result, final NodeHandler<T> handler) {
  708.         return result.isAttributeResult() ? result.getAttributeValue(handler) : handler.getValue(result.getNode());
  709.     }

  710.     /**
  711.      * Creates a {@code DefinedKeysVisitor} and visits all defined keys with it.
  712.      *
  713.      * @return the visitor after all keys have been visited
  714.      */
  715.     private DefinedKeysVisitor visitDefinedKeys() {
  716.         final DefinedKeysVisitor visitor = new DefinedKeysVisitor();
  717.         final NodeHandler<T> nodeHandler = getModel().getNodeHandler();
  718.         NodeTreeWalker.INSTANCE.walkDFS(nodeHandler.getRootNode(), visitor, nodeHandler);
  719.         return visitor;
  720.     }
  721. }