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}