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.interpol; 018 019 import java.util.HashMap; 020 import java.util.Map; 021 import java.util.Set; 022 023 import org.apache.commons.lang.text.StrLookup; 024 025 /** 026 * <p> 027 * A class that handles interpolation (variable substitution) for configuration 028 * objects. 029 * </p> 030 * <p> 031 * Each instance of {@code AbstractConfiguration} is associated with an 032 * object of this class. All interpolation tasks are delegated to this object. 033 * </p> 034 * <p> 035 * {@code ConfigurationInterpolator} works together with the 036 * {@code StrSubstitutor} class from <a 037 * href="http://commons.apache.org/lang">Commons Lang</a>. By extending 038 * {@code StrLookup} it is able to provide values for variables that 039 * appear in expressions. 040 * </p> 041 * <p> 042 * The basic idea of this class is that it can maintain a set of primitive 043 * {@code StrLookup} objects, each of which is identified by a special 044 * prefix. The variables to be processed have the form 045 * <code>${prefix:name}</code>. {@code ConfigurationInterpolator} will 046 * extract the prefix and determine, which primitive lookup object is registered 047 * for it. Then the name of the variable is passed to this object to obtain the 048 * actual value. It is also possible to define a default lookup object, which 049 * will be used for variables that do not have a prefix or that cannot be 050 * resolved by their associated lookup object. 051 * </p> 052 * <p> 053 * When a new instance of this class is created it is initialized with a default 054 * set of primitive lookup objects. This set can be customized using the static 055 * methods {@code registerGlobalLookup()} and 056 * {@code deregisterGlobalLookup()}. Per default it contains the 057 * following standard lookup objects: 058 * </p> 059 * <p> 060 * <table border="1"> 061 * <tr> 062 * <th>Prefix</th> 063 * <th>Lookup object</th> 064 * </tr> 065 * <tr> 066 * <td valign="top">sys</td> 067 * <td>With this prefix a lookup object is associated that is able to resolve 068 * system properties.</td> 069 * </tr> 070 * <tr> 071 * <td valign="top">const</td> 072 * <td>The {@code const} prefix indicates that a variable is to be 073 * interpreted as a constant member field of a class (i.e. a field with the 074 * <b>static final</b> modifiers). The name of the variable must be of the form 075 * {@code <full qualified class name>.<field name>}, e.g. 076 * {@code org.apache.commons.configuration.interpol.ConfigurationInterpolator.PREFIX_CONSTANTS}. 077 * </td> 078 * </tr> 079 * </table> 080 * </p> 081 * <p> 082 * After an instance has been created the current set of lookup objects can be 083 * modified using the {@code registerLookup()} and 084 * {@code deregisterLookup()} methods. The default lookup object (that is 085 * invoked for variables without a prefix) can be set with the 086 * {@code setDefaultLookup()} method. (If a 087 * {@code ConfigurationInterpolator} instance is created by a 088 * configuration object, this lookup points to the configuration itself, so that 089 * variables are resolved using the configuration's properties. This ensures 090 * backward compatibility to earlier version of Commons Configuration.) 091 * </p> 092 * <p> 093 * Implementation node: Instances of this class are not thread-safe related to 094 * modifications of their current set of registered lookup objects. It is 095 * intended that each instance is associated with a single 096 * {@code Configuration} object and used for its interpolation tasks. 097 * </p> 098 * 099 * @version $Id: ConfigurationInterpolator.java 1210217 2011-12-04 20:57:33Z oheger $ 100 * @since 1.4 101 * @author <a 102 * href="http://commons.apache.org/configuration/team-list.html">Commons 103 * Configuration team</a> 104 */ 105 public class ConfigurationInterpolator extends StrLookup 106 { 107 /** 108 * Constant for the prefix of the standard lookup object for resolving 109 * system properties. 110 */ 111 public static final String PREFIX_SYSPROPERTIES = "sys"; 112 113 /** 114 * Constant for the prefix of the standard lookup object for resolving 115 * constant values. 116 */ 117 public static final String PREFIX_CONSTANTS = "const"; 118 119 /** 120 * Constant for the prefix of the standard lookup object for resolving 121 * environment properties. 122 * @since 1.7 123 */ 124 public static final String PREFIX_ENVIRONMENT = "env"; 125 126 /** Constant for the prefix separator. */ 127 private static final char PREFIX_SEPARATOR = ':'; 128 129 /** A map with the globally registered lookup objects. */ 130 private static Map<String, StrLookup> globalLookups; 131 132 /** A map with the locally registered lookup objects. */ 133 private Map<String, StrLookup> localLookups; 134 135 /** Stores the default lookup object. */ 136 private StrLookup defaultLookup; 137 138 /** Stores a parent interpolator objects if the interpolator is nested hierarchically. */ 139 private ConfigurationInterpolator parentInterpolator; 140 141 /** 142 * Creates a new instance of {@code ConfigurationInterpolator}. 143 */ 144 public ConfigurationInterpolator() 145 { 146 synchronized (globalLookups) 147 { 148 localLookups = new HashMap<String, StrLookup>(globalLookups); 149 } 150 } 151 152 /** 153 * Registers the given lookup object for the specified prefix globally. This 154 * means that all instances that are created later will use this lookup 155 * object for this prefix. If for this prefix a lookup object is already 156 * registered, the new lookup object will replace the old one. Note that the 157 * lookup objects registered here will be shared between multiple clients. 158 * So they should be thread-safe. 159 * 160 * @param prefix the variable prefix (must not be <b>null</b>) 161 * @param lookup the lookup object to be used for this prefix (must not be 162 * <b>null</b>) 163 */ 164 public static void registerGlobalLookup(String prefix, StrLookup lookup) 165 { 166 if (prefix == null) 167 { 168 throw new IllegalArgumentException( 169 "Prefix for lookup object must not be null!"); 170 } 171 if (lookup == null) 172 { 173 throw new IllegalArgumentException( 174 "Lookup object must not be null!"); 175 } 176 synchronized (globalLookups) 177 { 178 globalLookups.put(prefix, lookup); 179 } 180 } 181 182 /** 183 * Deregisters the global lookup object for the specified prefix. This means 184 * that this lookup object won't be available for later created instances 185 * any more. For already existing instances this operation does not have any 186 * impact. 187 * 188 * @param prefix the variable prefix 189 * @return a flag whether for this prefix a lookup object had been 190 * registered 191 */ 192 public static boolean deregisterGlobalLookup(String prefix) 193 { 194 synchronized (globalLookups) 195 { 196 return globalLookups.remove(prefix) != null; 197 } 198 } 199 200 /** 201 * Registers the given lookup object for the specified prefix at this 202 * instance. From now on this lookup object will be used for variables that 203 * have the specified prefix. 204 * 205 * @param prefix the variable prefix (must not be <b>null</b>) 206 * @param lookup the lookup object to be used for this prefix (must not be 207 * <b>null</b>) 208 */ 209 public void registerLookup(String prefix, StrLookup lookup) 210 { 211 if (prefix == null) 212 { 213 throw new IllegalArgumentException( 214 "Prefix for lookup object must not be null!"); 215 } 216 if (lookup == null) 217 { 218 throw new IllegalArgumentException( 219 "Lookup object must not be null!"); 220 } 221 localLookups.put(prefix, lookup); 222 } 223 224 /** 225 * Deregisters the lookup object for the specified prefix at this instance. 226 * It will be removed from this instance. 227 * 228 * @param prefix the variable prefix 229 * @return a flag whether for this prefix a lookup object had been 230 * registered 231 */ 232 public boolean deregisterLookup(String prefix) 233 { 234 return localLookups.remove(prefix) != null; 235 } 236 237 /** 238 * Returns a set with the prefixes, for which lookup objects are registered 239 * at this instance. This means that variables with these prefixes can be 240 * processed. 241 * 242 * @return a set with the registered variable prefixes 243 */ 244 public Set<String> prefixSet() 245 { 246 return localLookups.keySet(); 247 } 248 249 /** 250 * Returns the default lookup object. 251 * 252 * @return the default lookup object 253 */ 254 public StrLookup getDefaultLookup() 255 { 256 return defaultLookup; 257 } 258 259 /** 260 * Sets the default lookup object. This lookup object will be used for all 261 * variables without a special prefix. If it is set to <b>null</b>, such 262 * variables won't be processed. 263 * 264 * @param defaultLookup the new default lookup object 265 */ 266 public void setDefaultLookup(StrLookup defaultLookup) 267 { 268 this.defaultLookup = defaultLookup; 269 } 270 271 /** 272 * Resolves the specified variable. This implementation will try to extract 273 * a variable prefix from the given variable name (the first colon (':') is 274 * used as prefix separator). It then passes the name of the variable with 275 * the prefix stripped to the lookup object registered for this prefix. If 276 * no prefix can be found or if the associated lookup object cannot resolve 277 * this variable, the default lookup object will be used. 278 * 279 * @param var the name of the variable whose value is to be looked up 280 * @return the value of this variable or <b>null</b> if it cannot be 281 * resolved 282 */ 283 @Override 284 public String lookup(String var) 285 { 286 if (var == null) 287 { 288 return null; 289 } 290 291 int prefixPos = var.indexOf(PREFIX_SEPARATOR); 292 if (prefixPos >= 0) 293 { 294 String prefix = var.substring(0, prefixPos); 295 String name = var.substring(prefixPos + 1); 296 String value = fetchLookupForPrefix(prefix).lookup(name); 297 if (value == null && getParentInterpolator() != null) 298 { 299 value = getParentInterpolator().fetchLookupForPrefix(prefix).lookup(name); 300 } 301 if (value != null) 302 { 303 return value; 304 } 305 } 306 String value = fetchNoPrefixLookup().lookup(var); 307 if (value == null && getParentInterpolator() != null) 308 { 309 value = getParentInterpolator().fetchNoPrefixLookup().lookup(var); 310 } 311 return value; 312 } 313 314 /** 315 * Returns the lookup object to be used for variables without a prefix. This 316 * implementation will check whether a default lookup object was set. If 317 * this is the case, it will be returned. Otherwise a <b>null</b> lookup 318 * object will be returned (never <b>null</b>). 319 * 320 * @return the lookup object to be used for variables without a prefix 321 */ 322 protected StrLookup fetchNoPrefixLookup() 323 { 324 return (getDefaultLookup() != null) ? getDefaultLookup() : StrLookup.noneLookup(); 325 } 326 327 /** 328 * Obtains the lookup object for the specified prefix. This method is called 329 * by the {@code lookup()} method. This implementation will check 330 * whether a lookup object is registered for the given prefix. If not, a 331 * <b>null</b> lookup object will be returned (never <b>null</b>). 332 * 333 * @param prefix the prefix 334 * @return the lookup object to be used for this prefix 335 */ 336 protected StrLookup fetchLookupForPrefix(String prefix) 337 { 338 StrLookup lookup = localLookups.get(prefix); 339 if (lookup == null) 340 { 341 lookup = StrLookup.noneLookup(); 342 } 343 return lookup; 344 } 345 346 /** 347 * Registers the local lookup instances for the given interpolator. 348 * 349 * @param interpolator the instance receiving the local lookups 350 * @since upcoming 351 */ 352 public void registerLocalLookups(ConfigurationInterpolator interpolator) 353 { 354 interpolator.localLookups.putAll(localLookups); 355 } 356 357 /** 358 * Sets the parent interpolator. This object is used if the interpolation is nested 359 * hierarchically and the current interpolation object cannot resolve a variable. 360 * 361 * @param parentInterpolator the parent interpolator object or <b>null</b> 362 * @since upcoming 363 */ 364 public void setParentInterpolator(ConfigurationInterpolator parentInterpolator) 365 { 366 this.parentInterpolator = parentInterpolator; 367 } 368 369 /** 370 * Requests the parent interpolator. This object is used if the interpolation is nested 371 * hierarchically and the current interpolation 372 * 373 * @return the parent interpolator or <b>null</b> 374 * @since upcoming 375 */ 376 public ConfigurationInterpolator getParentInterpolator() 377 { 378 return this.parentInterpolator; 379 } 380 381 // static initializer, sets up the map with the standard lookups 382 static 383 { 384 globalLookups = new HashMap<String, StrLookup>(); 385 globalLookups.put(PREFIX_SYSPROPERTIES, StrLookup.systemPropertiesLookup()); 386 globalLookups.put(PREFIX_CONSTANTS, new ConstantLookup()); 387 globalLookups.put(PREFIX_ENVIRONMENT, new EnvironmentLookup()); 388 } 389 }