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