    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.configuration2;
    018
    019import org.apache.commons.configuration2.tree.ImmutableNode;
    020import org.apache.commons.configuration2.tree.InMemoryNodeModel;
    021import org.apache.commons.configuration2.tree.InMemoryNodeModelSupport;
    022import org.apache.commons.configuration2.tree.NodeModel;
    023import org.apache.commons.configuration2.tree.NodeSelector;
    024import org.apache.commons.configuration2.tree.TrackedNodeModel;
    025
    026/**
    027 * <p>
    028 * A specialized hierarchical configuration class with a node model that uses a
    029 * tracked node of another node model as its root node.
    030 * </p>
    031 * <p>
    032 * Configurations of this type are initialized with a special {@link NodeModel}
    033 * operating on a specific tracked node of the parent configuration and the
    034 * corresponding {@link NodeSelector}. All property accessor methods are
    035 * evaluated relative to this root node. A good use case for a
    036 * {@code SubnodeConfiguration} is when multiple properties from a specific sub
    037 * tree of the whole configuration need to be accessed. Then a
    038 * {@code SubnodeConfiguration} can be created with the parent node of the
    039 * affected sub tree as root node. This allows for simpler property keys and is
    040 * also more efficient.
    041 * </p>
    042 * <p>
    043 * By making use of a tracked node as root node, a {@code SubnodeConfiguration}
    044 * and its parent configuration initially operate on the same hierarchy of
    045 * configuration nodes. So if modifications are performed at the subnode
    046 * configuration, these changes are immediately visible in the parent
    047 * configuration. Analogously will updates of the parent configuration affect
    048 * the {@code SubnodeConfiguration} if the sub tree spanned by the
    049 * {@code SubnodeConfiguration}'s root node is involved.
    050 * </p>
    051 * <p>
    052 * Note that by making use of a {@code NodeSelector} the
    053 * {@code SubnodeConfiguration} is not associated with a physical node instance,
    054 * but the selection criteria stored in the selector are evaluated after each
    055 * change of the nodes structure. As an example consider that the selector uses
    056 * a key with an index into a list element, say index 2. Now if an update occurs
    057 * on the underlying nodes structure which removes the first element in this
    058 * list structure, the {@code SubnodeConfiguration} still references the element
    059 * with index 2 which is now another one.
    060 * </p>
    061 * <p>
    062 * There are also possible changes of the underlying nodes structure which
    063 * completely detach the {@code SubnodeConfiguration} from its parent
    064 * configuration. For instance, the key referenced by the
    065 * {@code SubnodeConfiguration} could be removed in the parent configuration. If
    066 * this happens, the {@code SubnodeConfiguration} stays functional; however, it
    067 * now operates on a separate node model than its parent configuration. Changes
    068 * made by one configuration are no longer visible for the other one (as the
    069 * node models have no longer overlapping nodes, there is no way to have a
    070 * synchronization here).
    071 * </p>
    072 * <p>
    073 * When a subnode configuration is created, it inherits the settings of its
    074 * parent configuration, e.g. some flags like the
    075 * {@code throwExceptionOnMissing} flag or the settings for handling list
    076 * delimiters) or the expression engine. If these settings are changed later in
    077 * either the subnode or the parent configuration, the changes are not visible
    078 * for each other. So you could create a subnode configuration, and change its
    079 * expression engine without affecting the parent configuration.
    080 * </p>
    081 * <p>
    082 * Because the {@code SubnodeConfiguration} operates on the same nodes structure
    083 * as its parent it uses the same {@code Synchronizer} instance per default.
    084 * This means that locks held on one {@code SubnodeConfiguration} also impact
    085 * the parent configuration and all of its other {@code SubnodeConfiguration}
    086 * objects. You should not change this without a good reason! Otherwise, there
    087 * is the risk of data corruption when multiple threads access these
    088 * configuration concurrently.
    089 * </p>
    090 * <p>
    091 * From its purpose this class is quite similar to {@link SubsetConfiguration}.
    092 * The difference is that a subset configuration of a hierarchical configuration
    093 * may combine multiple configuration nodes from different sub trees of the
    094 * configuration, while all nodes in a subnode configuration belong to the same
    095 * sub tree. If an application can live with this limitation, it is recommended
    096 * to use this class instead of {@code SubsetConfiguration} because creating a
    097 * subset configuration is more expensive than creating a subnode configuration.
    098 * </p>
    099 * <p>
    100 * It is strongly recommended to create {@code SubnodeConfiguration} instances
    101 * only through the {@code configurationAt()} methods of a hierarchical
    102 * configuration. These methods ensure that all necessary initializations are
    103 * done. Creating instances manually without doing proper initialization may
    104 * break some of the functionality provided by this class.
    105 * </p>
    106 *
    107 * @since 1.3
    108 * @version $Id: SubnodeConfiguration.java 1842194 2018-09-27 22:24:23Z ggregory $
    109 */
    110public class SubnodeConfiguration extends BaseHierarchicalConfiguration
    111{
    112    /** Stores the parent configuration. */
    113    private final BaseHierarchicalConfiguration parent;
    114
    115    /** The node selector selecting the root node of this configuration. */
    116    private final NodeSelector rootSelector;
    117
    118    /**
    119     * Creates a new instance of {@code SubnodeConfiguration} and initializes it
    120     * with all relevant properties.
    121     *
    122     * @param parent the parent configuration
    123     * @param model the {@code TrackedNodeModel} to be used for this configuration
    124     * @throws IllegalArgumentException if a required argument is missing
    125     */
    126    public SubnodeConfiguration(final BaseHierarchicalConfiguration parent,
    127                                final TrackedNodeModel model)
    128    {
    129        super(model);
    130        if (parent == null)
    131        {
    132            throw new IllegalArgumentException(
    133                    "Parent configuration must not be null!");
    134        }
    135        if (model == null)
    136        {
    137            throw new IllegalArgumentException("Node model must not be null!");
    138        }
    139
    140        this.parent = parent;
    141        rootSelector = model.getSelector();
    142    }
    143
    144    /**
    145     * Returns the parent configuration of this subnode configuration.
    146     *
    147     * @return the parent configuration
    148     */
    149    public BaseHierarchicalConfiguration getParent()
    150    {
    151        return parent;
    152    }
    153
    154    /**
    155     * Returns the selector to the root node of this configuration.
    156     *
    157     * @return the {@code NodeSelector} to the root node
    158     */
    159    public NodeSelector getRootSelector()
    160    {
    161        return rootSelector;
    162    }
    163
    164    /**
    165     * Closes this sub configuration. This method closes the underlying
    166     * {@link TrackedNodeModel}, thus causing the tracked node acting as root
    167     * node to be released. Per default, this happens automatically when the
    168     * model is claimed by the garbage collector. By calling this method
    169     * explicitly, it can be indicated that this configuration is no longer used
    170     * and that resources used by it can be freed immediately.
    171     */
    172    public void close()
    173    {
    174        (getTrackedModel()).close();
    175    }
    176
    177    /**
    178     * {@inheritDoc} This implementation returns a newly created node model
    179     * with the correct root node set. Note that this model is not used for
    180     * property access, but only made available to clients that need to
    181     * operate on the node structure of this {@code SubnodeConfiguration}.
    182     * Be aware that the implementation of this method is not very efficient.
    183     */
    184    @Override
    185    public InMemoryNodeModel getNodeModel()
    186    {
    187        final ImmutableNode root =
    188                getParent().getNodeModel().getTrackedNode(getRootSelector());
    189        return new InMemoryNodeModel(root);
    190    }
    191
    192    /**
    193     * Returns the node model of the root configuration.
    194     * {@code SubnodeConfiguration} instances created from a hierarchical
    195     * configuration operate on the same node model, using different nodes as
    196     * their local root nodes. With this method the top-level node model can be
    197     * obtained. It works even in constellations where a
    198     * {@code SubnodeConfiguration} has been created from another
    199     * {@code SubnodeConfiguration}.
    200     *
    201     * @return the root node model
    202     * @since 2.2
    203     */
    204    public InMemoryNodeModel getRootNodeModel()
    205    {
    206        if (getParent() instanceof SubnodeConfiguration)
    207        {
    208            return ((SubnodeConfiguration) getParent()).getRootNodeModel();
    209        }
    210        return getParent().getNodeModel();
    211    }
    212
    213    /**
    214     * {@inheritDoc} This implementation returns a copy of the current node
    215     * model with the same settings. However, it has to be ensured that the
    216     * track count for the node selector is increased.
    217     *
    218     * @return the node model for the clone
    219     */
    220    @Override
    221    protected NodeModel<ImmutableNode> cloneNodeModel()
    222    {
    223        final InMemoryNodeModel parentModel =
    224                (InMemoryNodeModel) getParent().getModel();
    225        parentModel.trackNode(getRootSelector(), getParent());
    226        return new TrackedNodeModel(getParent(), getRootSelector(), true);
    227    }
    228
    229    /**
    230     * {@inheritDoc} This implementation returns a sub selector of the selector
    231     * of this configuration.
    232     */
    233    @Override
    234    protected NodeSelector getSubConfigurationNodeSelector(final String key)
    235    {
    236        return getRootSelector().subSelector(key);
    237    }
    238
    239    /**
    240     * {@inheritDoc} This implementation returns the parent model of the
    241     * {@link TrackedNodeModel} used by this configuration.
    242     */
    243    @Override
    244    protected InMemoryNodeModel getSubConfigurationParentModel()
    245    {
    246        return getTrackedModel().getParentModel();
    247    }
    248
    249    /**
    250     * {@inheritDoc} This implementation makes sure that the correct node model
    251     * (the one of the parent) is used for the new sub configuration.
    252     */
    253    @Override
    254    protected SubnodeConfiguration createSubConfigurationForTrackedNode(
    255            final NodeSelector selector, final InMemoryNodeModelSupport parentModelSupport)
    256    {
    257        return super.createSubConfigurationForTrackedNode(selector, getParent());
    258    }
    259
    260    /**
    261     * Convenience method that returns the tracked model used by this sub
    262     * configuration.
    263     *
    264     * @return the {@code TrackedNodeModel}
    265     */
    266    private TrackedNodeModel getTrackedModel()
    267    {
    268        return (TrackedNodeModel) getModel();
    269    }
    270}