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.configuration; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022 023import org.apache.commons.configuration.interpol.ConfigurationInterpolator; 024import org.apache.commons.configuration.reloading.Reloadable; 025import org.apache.commons.configuration.tree.ConfigurationNode; 026 027/** 028 * <p> 029 * A specialized hierarchical configuration class that wraps a single node of 030 * its parent configuration. 031 * </p> 032 * <p> 033 * Configurations of this type are initialized with a parent configuration and a 034 * configuration node of this configuration. This node becomes the root node of 035 * the subnode configuration. All property accessor methods are evaluated 036 * relative to this root node. A good use case for a 037 * {@code SubnodeConfiguration} is when multiple properties from a 038 * specific sub tree of the whole configuration need to be accessed. Then a 039 * {@code SubnodeConfiguration} can be created with the parent node of 040 * the affected sub tree as root node. This allows for simpler property keys and 041 * is also more efficient. 042 * </p> 043 * <p> 044 * A subnode configuration and its parent configuration operate on the same 045 * hierarchy of configuration nodes. So if modifications are performed at the 046 * subnode configuration, these changes are immediately visible in the parent 047 * configuration. Analogously will updates of the parent configuration affect 048 * the subnode configuration if the sub tree spanned by the subnode 049 * configuration's root node is involved. 050 * </p> 051 * <p> 052 * There are however changes at the parent configuration, which cause the 053 * subnode configuration to become detached. An example for such a change is a 054 * reload operation of a file-based configuration, which replaces all nodes of 055 * the parent configuration. The subnode configuration per default still 056 * references the old nodes. Another example are list structures: a subnode 057 * configuration can be created to point on the <em>i</em>th element of the 058 * list. Now list elements can be added or removed, so that the list elements' 059 * indices change. In such a scenario the subnode configuration would always 060 * point to the same list element, regardless of its current index. 061 * </p> 062 * <p> 063 * To solve these problems and make a subnode configuration aware of 064 * such structural changes of its parent, it is possible to associate a 065 * subnode configuration with a configuration key. This can be done by calling 066 * the {@code setSubnodeKey()} method. If here a key is set, the subnode 067 * configuration will evaluate it on each access, thus ensuring that it is 068 * always in sync with its parent. In this mode the subnode configuration really 069 * behaves like a live-view on its parent. The price for this is a decreased 070 * performance because now an additional evaluation has to be performed on each 071 * property access. So this mode should only be used if necessary; if for 072 * instance a subnode configuration is only used for a temporary convenient 073 * access to a complex configuration, there is no need to make it aware for 074 * structural changes of its parent. If a subnode configuration is created 075 * using the {@link HierarchicalConfiguration#configurationAt(String, boolean) 076 * configurationAt()} method of {@code HierarchicalConfiguration} 077 * (which should be the preferred way), with an additional boolean parameter it 078 * can be specified whether the resulting subnode configuration should be 079 * aware of structural changes or not. Then the configuration key will be 080 * automatically set. 081 * </p> 082 * <p> 083 * <em>Note:</em> At the moment support for creating a subnode configuration 084 * that is aware of structural changes of its parent from another subnode 085 * configuration (a "sub subnode configuration") is limited. This only works if 086 * <ol><li>the subnode configuration that serves as the parent for the new 087 * subnode configuration is itself associated with a configuration key and</li> 088 * <li>the key passed in to create the new subnode configuration is not too 089 * complex (if configuration keys are used that contain indices, a corresponding 090 * key that is valid from the parent configuration's point of view cannot be 091 * constructed).</li></ol> 092 * </p> 093 * <p> 094 * When a subnode configuration is created, it inherits the settings of its 095 * parent configuration, e.g. some flags like the 096 * {@code throwExceptionOnMissing} flag or the settings for handling list 097 * delimiters) or the expression engine. If these settings are changed later in 098 * either the subnode or the parent configuration, the changes are not visible 099 * for each other. So you could create a subnode configuration, change its 100 * expression engine without affecting the parent configuration. 101 * </p> 102 * <p> 103 * From its purpose this class is quite similar to 104 * {@link SubsetConfiguration}. The difference is that a subset 105 * configuration of a hierarchical configuration may combine multiple 106 * configuration nodes from different sub trees of the configuration, while all 107 * nodes in a subnode configuration belong to the same sub tree. If an 108 * application can live with this limitation, it is recommended to use this 109 * class instead of {@code SubsetConfiguration} because creating a subset 110 * configuration is more expensive than creating a subnode configuration. 111 * </p> 112 * 113 * @since 1.3 114 * @author <a 115 * href="http://commons.apache.org/configuration/team-list.html">Commons 116 * Configuration team</a> 117 * @version $Id: SubnodeConfiguration.java 1210178 2011-12-04 18:58:51Z oheger $ 118 */ 119public class SubnodeConfiguration extends HierarchicalReloadableConfiguration 120{ 121 /** 122 * The serial version UID. 123 */ 124 private static final long serialVersionUID = 3105734147019386480L; 125 126 /** Stores the parent configuration. */ 127 private HierarchicalConfiguration parent; 128 129 /** Stores the key that was used to construct this configuration.*/ 130 private String subnodeKey; 131 132 /** 133 * Creates a new instance of {@code SubnodeConfiguration} and 134 * initializes it with the parent configuration and the new root node. 135 * 136 * @param parent the parent configuration 137 * @param root the root node of this subnode configuration 138 */ 139 public SubnodeConfiguration(HierarchicalConfiguration parent, ConfigurationNode root) 140 { 141 super(parent instanceof Reloadable ? ((Reloadable) parent).getReloadLock() : null); 142 if (parent == null) 143 { 144 throw new IllegalArgumentException( 145 "Parent configuration must not be null!"); 146 } 147 if (root == null) 148 { 149 throw new IllegalArgumentException("Root node must not be null!"); 150 } 151 152 setRootNode(root); 153 this.parent = parent; 154 initFromParent(parent); 155 } 156 157 /** 158 * Returns the parent configuration of this subnode configuration. 159 * 160 * @return the parent configuration 161 */ 162 public HierarchicalConfiguration getParent() 163 { 164 return parent; 165 } 166 167 /** 168 * Returns the key that was used to construct this configuration. If here a 169 * non-<b>null</b> value is returned, the subnode configuration will 170 * always check its parent for structural changes and reconstruct itself if 171 * necessary. 172 * 173 * @return the key for selecting this configuration's root node 174 * @since 1.5 175 */ 176 public String getSubnodeKey() 177 { 178 return subnodeKey; 179 } 180 181 /** 182 * Sets the key to the root node of this subnode configuration. If here a 183 * key is set, the subnode configuration will behave like a live-view on its 184 * parent for this key. See the class comment for more details. 185 * 186 * @param subnodeKey the key used to construct this configuration 187 * @since 1.5 188 */ 189 public void setSubnodeKey(String subnodeKey) 190 { 191 this.subnodeKey = subnodeKey; 192 } 193 194 /** 195 * Returns the root node for this configuration. If a subnode key is set, 196 * this implementation re-evaluates this key to find out if this subnode 197 * configuration needs to be reconstructed. This ensures that the subnode 198 * configuration is always synchronized with its parent configuration. 199 * 200 * @return the root node of this configuration 201 * @since 1.5 202 * @see #setSubnodeKey(String) 203 */ 204 @Override 205 public ConfigurationNode getRootNode() 206 { 207 if (getSubnodeKey() != null) 208 { 209 try 210 { 211 List<ConfigurationNode> nodes = getParent().fetchNodeList(getSubnodeKey()); 212 if (nodes.size() != 1) 213 { 214 // key is invalid, so detach this subnode configuration 215 setSubnodeKey(null); 216 } 217 else 218 { 219 ConfigurationNode currentRoot = nodes.get(0); 220 if (currentRoot != super.getRootNode()) 221 { 222 // the root node was changed due to a change of the 223 // parent 224 fireEvent(EVENT_SUBNODE_CHANGED, null, null, true); 225 setRootNode(currentRoot); 226 fireEvent(EVENT_SUBNODE_CHANGED, null, null, false); 227 } 228 return currentRoot; 229 } 230 } 231 catch (Exception ex) 232 { 233 // Evaluation of the key caused an exception. Probably the 234 // expression engine has changed on the parent. Detach this 235 // configuration, there is not much we can do about this. 236 setSubnodeKey(null); 237 } 238 } 239 240 return super.getRootNode(); // use stored root node 241 } 242 243 /** 244 * Returns a hierarchical configuration object for the given sub node. 245 * This implementation will ensure that the returned 246 * {@code SubnodeConfiguration} object will have the same parent than 247 * this object. 248 * 249 * @param node the sub node, for which the configuration is to be created 250 * @return a hierarchical configuration for this sub node 251 */ 252 @Override 253 protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node) 254 { 255 SubnodeConfiguration result = new SubnodeConfiguration(getParent(), node); 256 getParent().registerSubnodeConfiguration(result); 257 return result; 258 } 259 260 /** 261 * Returns a hierarchical configuration object for the given sub node that 262 * is aware of structural changes of its parent. Works like the method with 263 * the same name, but also sets the subnode key for the new subnode 264 * configuration, so it can check whether the parent has been changed. This 265 * only works if this subnode configuration has itself a valid subnode key. 266 * So if a subnode configuration that should be aware of structural changes 267 * is created from an already existing subnode configuration, this subnode 268 * configuration must also be aware of such changes. 269 * 270 * @param node the sub node, for which the configuration is to be created 271 * @param subnodeKey the construction key 272 * @return a hierarchical configuration for this sub node 273 * @since 1.5 274 */ 275 @Override 276 protected SubnodeConfiguration createSubnodeConfiguration( 277 ConfigurationNode node, String subnodeKey) 278 { 279 SubnodeConfiguration result = createSubnodeConfiguration(node); 280 281 if (getSubnodeKey() != null) 282 { 283 // construct the correct subnode key 284 // determine path to root node 285 List<ConfigurationNode> lstPathToRoot = new ArrayList<ConfigurationNode>(); 286 ConfigurationNode top = super.getRootNode(); 287 ConfigurationNode nd = node; 288 while (nd != top) 289 { 290 lstPathToRoot.add(nd); 291 nd = nd.getParentNode(); 292 } 293 294 // construct the keys for the nodes on this path 295 Collections.reverse(lstPathToRoot); 296 String key = getSubnodeKey(); 297 for (ConfigurationNode pathNode : lstPathToRoot) 298 { 299 key = getParent().getExpressionEngine().nodeKey(pathNode, key); 300 } 301 result.setSubnodeKey(key); 302 } 303 304 return result; 305 } 306 307 /** 308 * Creates a new node. This task is delegated to the parent. 309 * 310 * @param name the node's name 311 * @return the new node 312 */ 313 @Override 314 protected Node createNode(String name) 315 { 316 return getParent().createNode(name); 317 } 318 319 /** 320 * Initializes this subnode configuration from the given parent 321 * configuration. This method is called by the constructor. It will copy 322 * many settings from the parent. 323 * 324 * @param parentConfig the parent configuration 325 */ 326 protected void initFromParent(HierarchicalConfiguration parentConfig) 327 { 328 setExpressionEngine(parentConfig.getExpressionEngine()); 329 setListDelimiter(parentConfig.getListDelimiter()); 330 setDelimiterParsingDisabled(parentConfig.isDelimiterParsingDisabled()); 331 setThrowExceptionOnMissing(parentConfig.isThrowExceptionOnMissing()); 332 } 333 334 /** 335 * Creates a ConfigurationInterpolator with a chain to the parent's 336 * interpolator. 337 * 338 * @return the new interpolator 339 */ 340 @Override 341 protected ConfigurationInterpolator createInterpolator() 342 { 343 ConfigurationInterpolator interpolator = super.createInterpolator(); 344 interpolator.setParentInterpolator(getParent().getInterpolator()); 345 return interpolator; 346 } 347}