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 1806832 2017-08-31 17:51:39Z oheger $
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(BaseHierarchicalConfiguration parent,
127                                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        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        else
211        {
212            return getParent().getNodeModel();
213        }
214    }
215
216    /**
217     * {@inheritDoc} This implementation returns a copy of the current node
218     * model with the same settings. However, it has to be ensured that the
219     * track count for the node selector is increased.
220     *
221     * @return the node model for the clone
222     */
223    @Override
224    protected NodeModel<ImmutableNode> cloneNodeModel()
225    {
226        InMemoryNodeModel parentModel =
227                (InMemoryNodeModel) getParent().getModel();
228        parentModel.trackNode(getRootSelector(), getParent());
229        return new TrackedNodeModel(getParent(), getRootSelector(), true);
230    }
231
232    /**
233     * {@inheritDoc} This implementation returns a sub selector of the selector
234     * of this configuration.
235     */
236    @Override
237    protected NodeSelector getSubConfigurationNodeSelector(String key)
238    {
239        return getRootSelector().subSelector(key);
240    }
241
242    /**
243     * {@inheritDoc} This implementation returns the parent model of the
244     * {@link TrackedNodeModel} used by this configuration.
245     */
246    @Override
247    protected InMemoryNodeModel getSubConfigurationParentModel()
248    {
249        return getTrackedModel().getParentModel();
250    }
251
252    /**
253     * {@inheritDoc} This implementation makes sure that the correct node model
254     * (the one of the parent) is used for the new sub configuration.
255     */
256    @Override
257    protected SubnodeConfiguration createSubConfigurationForTrackedNode(
258            NodeSelector selector, InMemoryNodeModelSupport parentModelSupport)
259    {
260        return super.createSubConfigurationForTrackedNode(selector, getParent());
261    }
262
263    /**
264     * Convenience method that returns the tracked model used by this sub
265     * configuration.
266     *
267     * @return the {@code TrackedNodeModel}
268     */
269    private TrackedNodeModel getTrackedModel()
270    {
271        return (TrackedNodeModel) getModel();
272    }
273}