| /* |
| * 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.NodeModel; |
| import org.apache.commons.configuration2.tree.NodeSelector; |
| import org.apache.commons.configuration2.tree.TrackedNodeModel; |
| |
| /** |
| * <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 |
| * @version $Id$ |
| */ |
| public class SubnodeConfiguration extends BaseHierarchicalConfiguration |
| { |
| /** Stores the parent configuration. */ |
| private final BaseHierarchicalConfiguration parent; |
| |
| /** The node selector selecting the root node of this configuration. */ |
| private final NodeSelector rootSelector; |
| |
| /** |
| * 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(BaseHierarchicalConfiguration parent, |
| 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. |
| * |
| * @return the parent configuration |
| */ |
| public BaseHierarchicalConfiguration getParent() |
| { |
| return parent; |
| } |
| |
| /** |
| * 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 |
| * {@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 the node model of the parent |
| * configuration. This is necessary because this sub configuration does not |
| * have an {@code InMemoryNodeModel} object. |
| */ |
| @Override |
| public InMemoryNodeModel getNodeModel() |
| { |
| 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. |
| * |
| * @return the node model for the clone |
| */ |
| @Override |
| protected NodeModel<ImmutableNode> cloneNodeModel() |
| { |
| 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. |
| */ |
| @Override |
| protected NodeSelector getSubConfigurationNodeSelector(String key) |
| { |
| return getRootSelector().subSelector(key); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation returns the parent model of the |
| * {@link TrackedNodeModel} used by this configuration. |
| */ |
| @Override |
| protected InMemoryNodeModel getSubConfigurationParentModel() |
| { |
| return getTrackedModel().getParentModel(); |
| } |
| |
| /** |
| * Convenience method that returns the tracked model used by this sub |
| * configuration. |
| * |
| * @return the {@code TrackedNodeModel} |
| */ |
| private TrackedNodeModel getTrackedModel() |
| { |
| return (TrackedNodeModel) getModel(); |
| } |
| } |