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

import org.apache.commons.configuration2.tree.ImmutableNode;
import org.apache.commons.configuration2.tree.InMemoryNodeModel;
import org.apache.commons.configuration2.tree.InMemoryNodeModelSupport;
import org.apache.commons.configuration2.tree.NodeModel;
import org.apache.commons.configuration2.tree.NodeSelector;
import org.apache.commons.configuration2.tree.TrackedNodeModel;

A specialized hierarchical configuration class with a node model that uses a tracked node of another node model as its root node.

Configurations of this type are initialized with a special NodeModel operating on a specific tracked node of the parent configuration and the corresponding NodeSelector. All property accessor methods are evaluated relative to this root node. A good use case for a SubnodeConfiguration is when multiple properties from a specific sub tree of the whole configuration need to be accessed. Then a SubnodeConfiguration can be created with the parent node of the affected sub tree as root node. This allows for simpler property keys and is also more efficient.

By making use of a tracked node as root node, a SubnodeConfiguration and its parent configuration initially operate on the same hierarchy of configuration nodes. So if modifications are performed at the subnode configuration, these changes are immediately visible in the parent configuration. Analogously will updates of the parent configuration affect the SubnodeConfiguration if the sub tree spanned by the SubnodeConfiguration's root node is involved.

Note that by making use of a NodeSelector the SubnodeConfiguration is not associated with a physical node instance, but the selection criteria stored in the selector are evaluated after each change of the nodes structure. As an example consider that the selector uses a key with an index into a list element, say index 2. Now if an update occurs on the underlying nodes structure which removes the first element in this list structure, the SubnodeConfiguration still references the element with index 2 which is now another one.

There are also possible changes of the underlying nodes structure which completely detach the SubnodeConfiguration from its parent configuration. For instance, the key referenced by the SubnodeConfiguration could be removed in the parent configuration. If this happens, the SubnodeConfiguration stays functional; however, it now operates on a separate node model than its parent configuration. Changes made by one configuration are no longer visible for the other one (as the node models have no longer overlapping nodes, there is no way to have a synchronization here).

When a subnode configuration is created, it inherits the settings of its parent configuration, e.g. some flags like the throwExceptionOnMissing flag or the settings for handling list delimiters) or the expression engine. If these settings are changed later in either the subnode or the parent configuration, the changes are not visible for each other. So you could create a subnode configuration, and change its expression engine without affecting the parent configuration.

Because the SubnodeConfiguration operates on the same nodes structure as its parent it uses the same Synchronizer instance per default. This means that locks held on one SubnodeConfiguration also impact the parent configuration and all of its other SubnodeConfiguration objects. You should not change this without a good reason! Otherwise, there is the risk of data corruption when multiple threads access these configuration concurrently.

From its purpose this class is quite similar to SubsetConfiguration. The difference is that a subset configuration of a hierarchical configuration may combine multiple configuration nodes from different sub trees of the configuration, while all nodes in a subnode configuration belong to the same sub tree. If an application can live with this limitation, it is recommended to use this class instead of SubsetConfiguration because creating a subset configuration is more expensive than creating a subnode configuration.

It is strongly recommended to create SubnodeConfiguration instances only through the configurationAt() methods of a hierarchical configuration. These methods ensure that all necessary initializations are done. Creating instances manually without doing proper initialization may break some of the functionality provided by this class.

Since:1.3
/** * <p> * A specialized hierarchical configuration class with a node model that uses a * tracked node of another node model as its root node. * </p> * <p> * Configurations of this type are initialized with a special {@link NodeModel} * operating on a specific tracked node of the parent configuration and the * corresponding {@link NodeSelector}. All property accessor methods are * evaluated relative to this root node. A good use case for a * {@code SubnodeConfiguration} is when multiple properties from a specific sub * tree of the whole configuration need to be accessed. Then a * {@code SubnodeConfiguration} can be created with the parent node of the * affected sub tree as root node. This allows for simpler property keys and is * also more efficient. * </p> * <p> * By making use of a tracked node as root node, a {@code SubnodeConfiguration} * and its parent configuration initially operate on the same hierarchy of * configuration nodes. So if modifications are performed at the subnode * configuration, these changes are immediately visible in the parent * configuration. Analogously will updates of the parent configuration affect * the {@code SubnodeConfiguration} if the sub tree spanned by the * {@code SubnodeConfiguration}'s root node is involved. * </p> * <p> * Note that by making use of a {@code NodeSelector} the * {@code SubnodeConfiguration} is not associated with a physical node instance, * but the selection criteria stored in the selector are evaluated after each * change of the nodes structure. As an example consider that the selector uses * a key with an index into a list element, say index 2. Now if an update occurs * on the underlying nodes structure which removes the first element in this * list structure, the {@code SubnodeConfiguration} still references the element * with index 2 which is now another one. * </p> * <p> * There are also possible changes of the underlying nodes structure which * completely detach the {@code SubnodeConfiguration} from its parent * configuration. For instance, the key referenced by the * {@code SubnodeConfiguration} could be removed in the parent configuration. If * this happens, the {@code SubnodeConfiguration} stays functional; however, it * now operates on a separate node model than its parent configuration. Changes * made by one configuration are no longer visible for the other one (as the * node models have no longer overlapping nodes, there is no way to have a * synchronization here). * </p> * <p> * When a subnode configuration is created, it inherits the settings of its * parent configuration, e.g. some flags like the * {@code throwExceptionOnMissing} flag or the settings for handling list * delimiters) or the expression engine. If these settings are changed later in * either the subnode or the parent configuration, the changes are not visible * for each other. So you could create a subnode configuration, and change its * expression engine without affecting the parent configuration. * </p> * <p> * Because the {@code SubnodeConfiguration} operates on the same nodes structure * as its parent it uses the same {@code Synchronizer} instance per default. * This means that locks held on one {@code SubnodeConfiguration} also impact * the parent configuration and all of its other {@code SubnodeConfiguration} * objects. You should not change this without a good reason! Otherwise, there * is the risk of data corruption when multiple threads access these * configuration concurrently. * </p> * <p> * From its purpose this class is quite similar to {@link SubsetConfiguration}. * The difference is that a subset configuration of a hierarchical configuration * may combine multiple configuration nodes from different sub trees of the * configuration, while all nodes in a subnode configuration belong to the same * sub tree. If an application can live with this limitation, it is recommended * to use this class instead of {@code SubsetConfiguration} because creating a * subset configuration is more expensive than creating a subnode configuration. * </p> * <p> * It is strongly recommended to create {@code SubnodeConfiguration} instances * only through the {@code configurationAt()} methods of a hierarchical * configuration. These methods ensure that all necessary initializations are * done. Creating instances manually without doing proper initialization may * break some of the functionality provided by this class. * </p> * * @since 1.3 */
public class SubnodeConfiguration extends BaseHierarchicalConfiguration {
Stores the parent configuration.
/** Stores the parent configuration. */
private final BaseHierarchicalConfiguration parent;
The node selector selecting the root node of this configuration.
/** The node selector selecting the root node of this configuration. */
private final NodeSelector rootSelector;
Creates a new instance of SubnodeConfiguration and initializes it with all relevant properties.
Params:
  • parent – the parent configuration
  • model – the TrackedNodeModel to be used for this configuration
Throws:
/** * Creates a new instance of {@code SubnodeConfiguration} and initializes it * with all relevant properties. * * @param parent the parent configuration * @param model the {@code TrackedNodeModel} to be used for this configuration * @throws IllegalArgumentException if a required argument is missing */
public SubnodeConfiguration(final BaseHierarchicalConfiguration parent, final TrackedNodeModel model) { super(model); if (parent == null) { throw new IllegalArgumentException( "Parent configuration must not be null!"); } if (model == null) { throw new IllegalArgumentException("Node model must not be null!"); } this.parent = parent; rootSelector = model.getSelector(); }
Returns the parent configuration of this subnode configuration.
Returns:the parent configuration
/** * Returns the parent configuration of this subnode configuration. * * @return the parent configuration */
public BaseHierarchicalConfiguration getParent() { return parent; }
Returns the selector to the root node of this configuration.
Returns:the NodeSelector to the root node
/** * Returns the selector to the root node of this configuration. * * @return the {@code NodeSelector} to the root node */
public NodeSelector getRootSelector() { return rootSelector; }
Closes this sub configuration. This method closes the underlying TrackedNodeModel, thus causing the tracked node acting as root node to be released. Per default, this happens automatically when the model is claimed by the garbage collector. By calling this method explicitly, it can be indicated that this configuration is no longer used and that resources used by it can be freed immediately.
/** * Closes this sub configuration. This method closes the underlying * {@link TrackedNodeModel}, thus causing the tracked node acting as root * node to be released. Per default, this happens automatically when the * model is claimed by the garbage collector. By calling this method * explicitly, it can be indicated that this configuration is no longer used * and that resources used by it can be freed immediately. */
public void close() { getTrackedModel().close(); }
{@inheritDoc} This implementation returns a newly created node model with the correct root node set. Note that this model is not used for property access, but only made available to clients that need to operate on the node structure of this SubnodeConfiguration. Be aware that the implementation of this method is not very efficient.
/** * {@inheritDoc} This implementation returns a newly created node model * with the correct root node set. Note that this model is not used for * property access, but only made available to clients that need to * operate on the node structure of this {@code SubnodeConfiguration}. * Be aware that the implementation of this method is not very efficient. */
@Override public InMemoryNodeModel getNodeModel() { final ImmutableNode root = getParent().getNodeModel().getTrackedNode(getRootSelector()); return new InMemoryNodeModel(root); }
Returns the node model of the root configuration. SubnodeConfiguration instances created from a hierarchical configuration operate on the same node model, using different nodes as their local root nodes. With this method the top-level node model can be obtained. It works even in constellations where a SubnodeConfiguration has been created from another SubnodeConfiguration.
Returns:the root node model
Since:2.2
/** * Returns the node model of the root configuration. * {@code SubnodeConfiguration} instances created from a hierarchical * configuration operate on the same node model, using different nodes as * their local root nodes. With this method the top-level node model can be * obtained. It works even in constellations where a * {@code SubnodeConfiguration} has been created from another * {@code SubnodeConfiguration}. * * @return the root node model * @since 2.2 */
public InMemoryNodeModel getRootNodeModel() { if (getParent() instanceof SubnodeConfiguration) { return ((SubnodeConfiguration) getParent()).getRootNodeModel(); } return getParent().getNodeModel(); }
{@inheritDoc} This implementation returns a copy of the current node model with the same settings. However, it has to be ensured that the track count for the node selector is increased.
Returns:the node model for the clone
/** * {@inheritDoc} This implementation returns a copy of the current node * model with the same settings. However, it has to be ensured that the * track count for the node selector is increased. * * @return the node model for the clone */
@Override protected NodeModel<ImmutableNode> cloneNodeModel() { final InMemoryNodeModel parentModel = (InMemoryNodeModel) getParent().getModel(); parentModel.trackNode(getRootSelector(), getParent()); return new TrackedNodeModel(getParent(), getRootSelector(), true); }
{@inheritDoc} This implementation returns a sub selector of the selector of this configuration.
/** * {@inheritDoc} This implementation returns a sub selector of the selector * of this configuration. */
@Override protected NodeSelector getSubConfigurationNodeSelector(final String key) { return getRootSelector().subSelector(key); }
{@inheritDoc} This implementation returns the parent model of the TrackedNodeModel used by this configuration.
/** * {@inheritDoc} This implementation returns the parent model of the * {@link TrackedNodeModel} used by this configuration. */
@Override protected InMemoryNodeModel getSubConfigurationParentModel() { return getTrackedModel().getParentModel(); }
{@inheritDoc} This implementation makes sure that the correct node model (the one of the parent) is used for the new sub configuration.
/** * {@inheritDoc} This implementation makes sure that the correct node model * (the one of the parent) is used for the new sub configuration. */
@Override protected SubnodeConfiguration createSubConfigurationForTrackedNode( final NodeSelector selector, final InMemoryNodeModelSupport parentModelSupport) { return super.createSubConfigurationForTrackedNode(selector, getParent()); }
Convenience method that returns the tracked model used by this sub configuration.
Returns:the TrackedNodeModel
/** * Convenience method that returns the tracked model used by this sub * configuration. * * @return the {@code TrackedNodeModel} */
private TrackedNodeModel getTrackedModel() { return (TrackedNodeModel) getModel(); } }