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 */ 109public class SubnodeConfiguration extends BaseHierarchicalConfiguration 110{ 111 /** Stores the parent configuration. */ 112 private final BaseHierarchicalConfiguration parent; 113 114 /** The node selector selecting the root node of this configuration. */ 115 private final NodeSelector rootSelector; 116 117 /** 118 * Creates a new instance of {@code SubnodeConfiguration} and initializes it 119 * with all relevant properties. 120 * 121 * @param parent the parent configuration 122 * @param model the {@code TrackedNodeModel} to be used for this configuration 123 * @throws IllegalArgumentException if a required argument is missing 124 */ 125 public SubnodeConfiguration(final BaseHierarchicalConfiguration parent, 126 final TrackedNodeModel model) 127 { 128 super(model); 129 if (parent == null) 130 { 131 throw new IllegalArgumentException( 132 "Parent configuration must not be null!"); 133 } 134 if (model == null) 135 { 136 throw new IllegalArgumentException("Node model must not be null!"); 137 } 138 139 this.parent = parent; 140 rootSelector = model.getSelector(); 141 } 142 143 /** 144 * Returns the parent configuration of this subnode configuration. 145 * 146 * @return the parent configuration 147 */ 148 public BaseHierarchicalConfiguration getParent() 149 { 150 return parent; 151 } 152 153 /** 154 * Returns the selector to the root node of this configuration. 155 * 156 * @return the {@code NodeSelector} to the root node 157 */ 158 public NodeSelector getRootSelector() 159 { 160 return rootSelector; 161 } 162 163 /** 164 * Closes this sub configuration. This method closes the underlying 165 * {@link TrackedNodeModel}, thus causing the tracked node acting as root 166 * node to be released. Per default, this happens automatically when the 167 * model is claimed by the garbage collector. By calling this method 168 * explicitly, it can be indicated that this configuration is no longer used 169 * and that resources used by it can be freed immediately. 170 */ 171 public void close() 172 { 173 getTrackedModel().close(); 174 } 175 176 /** 177 * {@inheritDoc} This implementation returns a newly created node model 178 * with the correct root node set. Note that this model is not used for 179 * property access, but only made available to clients that need to 180 * operate on the node structure of this {@code SubnodeConfiguration}. 181 * Be aware that the implementation of this method is not very efficient. 182 */ 183 @Override 184 public InMemoryNodeModel getNodeModel() 185 { 186 final ImmutableNode root = 187 getParent().getNodeModel().getTrackedNode(getRootSelector()); 188 return new InMemoryNodeModel(root); 189 } 190 191 /** 192 * Returns the node model of the root configuration. 193 * {@code SubnodeConfiguration} instances created from a hierarchical 194 * configuration operate on the same node model, using different nodes as 195 * their local root nodes. With this method the top-level node model can be 196 * obtained. It works even in constellations where a 197 * {@code SubnodeConfiguration} has been created from another 198 * {@code SubnodeConfiguration}. 199 * 200 * @return the root node model 201 * @since 2.2 202 */ 203 public InMemoryNodeModel getRootNodeModel() 204 { 205 if (getParent() instanceof SubnodeConfiguration) 206 { 207 return ((SubnodeConfiguration) getParent()).getRootNodeModel(); 208 } 209 return getParent().getNodeModel(); 210 } 211 212 /** 213 * {@inheritDoc} This implementation returns a copy of the current node 214 * model with the same settings. However, it has to be ensured that the 215 * track count for the node selector is increased. 216 * 217 * @return the node model for the clone 218 */ 219 @Override 220 protected NodeModel<ImmutableNode> cloneNodeModel() 221 { 222 final InMemoryNodeModel parentModel = 223 (InMemoryNodeModel) getParent().getModel(); 224 parentModel.trackNode(getRootSelector(), getParent()); 225 return new TrackedNodeModel(getParent(), getRootSelector(), true); 226 } 227 228 /** 229 * {@inheritDoc} This implementation returns a sub selector of the selector 230 * of this configuration. 231 */ 232 @Override 233 protected NodeSelector getSubConfigurationNodeSelector(final String key) 234 { 235 return getRootSelector().subSelector(key); 236 } 237 238 /** 239 * {@inheritDoc} This implementation returns the parent model of the 240 * {@link TrackedNodeModel} used by this configuration. 241 */ 242 @Override 243 protected InMemoryNodeModel getSubConfigurationParentModel() 244 { 245 return getTrackedModel().getParentModel(); 246 } 247 248 /** 249 * {@inheritDoc} This implementation makes sure that the correct node model 250 * (the one of the parent) is used for the new sub configuration. 251 */ 252 @Override 253 protected SubnodeConfiguration createSubConfigurationForTrackedNode( 254 final NodeSelector selector, final InMemoryNodeModelSupport parentModelSupport) 255 { 256 return super.createSubConfigurationForTrackedNode(selector, getParent()); 257 } 258 259 /** 260 * Convenience method that returns the tracked model used by this sub 261 * configuration. 262 * 263 * @return the {@code TrackedNodeModel} 264 */ 265 private TrackedNodeModel getTrackedModel() 266 { 267 return (TrackedNodeModel) getModel(); 268 } 269}