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</code> is associated with an 032 * object of this class. All interpolation tasks are delegated to this object. 033 * </p> 034 * <p> 035 * <code>ConfigurationInterpolator</code> works together with the 036 * <code>StrSubstitutor</code> class from <a 037 * href="http://commons.apache.org/lang">Commons Lang</a>. By extending 038 * <code>StrLookup</code> 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</code> 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</code> 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()</code> and 056 * <code>deregisterGlobalLookup()</code>. 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</code> 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></code>, e.g. 076 * <code>org.apache.commons.configuration.interpol.ConfigurationInterpolator.PREFIX_CONSTANTS 077 * </code>.</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()</code> and 084 * <code>deregisterLookup()</code> methods. The default lookup object (that is 085 * invoked for variables without a prefix) can be set with the 086 * <code>setDefaultLookup()</code> method. (If a 087 * <code>ConfigurationInterpolator</code> 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</code> object and used for its interpolation tasks. 097 * </p> 098 * 099 * @version $Id: ConfigurationInterpolator.java 561230 2007-07-31 04:17:09Z rahul $ 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 /** Constant for the prefix separator. */ 120 private static final char PREFIX_SEPARATOR = ':'; 121 122 /** A map with the globally registered lookup objects. */ 123 private static Map globalLookups; 124 125 /** A map with the locally registered lookup objects. */ 126 private Map localLookups; 127 128 /** Stores the default lookup object. */ 129 private StrLookup defaultLookup; 130 131 /** 132 * Creates a new instance of <code>ConfigurationInterpolator</code>. 133 */ 134 public ConfigurationInterpolator() 135 { 136 synchronized (globalLookups) 137 { 138 localLookups = new HashMap(globalLookups); 139 } 140 } 141 142 /** 143 * Registers the given lookup object for the specified prefix globally. This 144 * means that all instances that are created later will use this lookup 145 * object for this prefix. If for this prefix a lookup object is already 146 * registered, the new lookup object will replace the old one. Note that the 147 * lookup objects registered here will be shared between multiple clients. 148 * So they should be thread-safe. 149 * 150 * @param prefix the variable prefix (must not be <b>null</b>) 151 * @param lookup the lookup object to be used for this prefix (must not be 152 * <b>null</b>) 153 */ 154 public static void registerGlobalLookup(String prefix, StrLookup lookup) 155 { 156 if (prefix == null) 157 { 158 throw new IllegalArgumentException( 159 "Prefix for lookup object must not be null!"); 160 } 161 if (lookup == null) 162 { 163 throw new IllegalArgumentException( 164 "Lookup object must not be null!"); 165 } 166 synchronized (globalLookups) 167 { 168 globalLookups.put(prefix, lookup); 169 } 170 } 171 172 /** 173 * Deregisters the global lookup object for the specified prefix. This means 174 * that this lookup object won't be available for later created instances 175 * any more. For already existing instances this operation does not have any 176 * impact. 177 * 178 * @param prefix the variable prefix 179 * @return a flag whether for this prefix a lookup object had been 180 * registered 181 */ 182 public static boolean deregisterGlobalLookup(String prefix) 183 { 184 synchronized (globalLookups) 185 { 186 return globalLookups.remove(prefix) != null; 187 } 188 } 189 190 /** 191 * Registers the given lookup object for the specified prefix at this 192 * instance. From now on this lookup object will be used for variables that 193 * have the specified prefix. 194 * 195 * @param prefix the variable prefix (must not be <b>null</b>) 196 * @param lookup the lookup object to be used for this prefix (must not be 197 * <b>null</b>) 198 */ 199 public void registerLookup(String prefix, StrLookup lookup) 200 { 201 if (prefix == null) 202 { 203 throw new IllegalArgumentException( 204 "Prefix for lookup object must not be null!"); 205 } 206 if (lookup == null) 207 { 208 throw new IllegalArgumentException( 209 "Lookup object must not be null!"); 210 } 211 localLookups.put(prefix, lookup); 212 } 213 214 /** 215 * Deregisters the lookup object for the specified prefix at this instance. 216 * It will be removed from this instance. 217 * 218 * @param prefix the variable prefix 219 * @return a flag whether for this prefix a lookup object had been 220 * registered 221 */ 222 public boolean deregisterLookup(String prefix) 223 { 224 return localLookups.remove(prefix) != null; 225 } 226 227 /** 228 * Returns a set with the prefixes, for which lookup objects are registered 229 * at this instance. This means that variables with these prefixes can be 230 * processed. 231 * 232 * @return a set with the registered variable prefixes 233 */ 234 public Set prefixSet() 235 { 236 return localLookups.keySet(); 237 } 238 239 /** 240 * Returns the default lookup object. 241 * 242 * @return the default lookup object 243 */ 244 public StrLookup getDefaultLookup() 245 { 246 return defaultLookup; 247 } 248 249 /** 250 * Sets the default lookup object. This lookup object will be used for all 251 * variables without a special prefix. If it is set to <b>null</b>, such 252 * variables won't be processed. 253 * 254 * @param defaultLookup the new default lookup object 255 */ 256 public void setDefaultLookup(StrLookup defaultLookup) 257 { 258 this.defaultLookup = defaultLookup; 259 } 260 261 /** 262 * Resolves the specified variable. This implementation will try to extract 263 * a variable prefix from the given variable name (the first colon (':') is 264 * used as prefix separator). It then passes the name of the variable with 265 * the prefix stripped to the lookup object registered for this prefix. If 266 * no prefix can be found or if the associated lookup object cannot resolve 267 * this variable, the default lookup object will be used. 268 * 269 * @param var the name of the variable whose value is to be looked up 270 * @return the value of this variable or <b>null</b> if it cannot be 271 * resolved 272 */ 273 public String lookup(String var) 274 { 275 if (var == null) 276 { 277 return null; 278 } 279 280 int prefixPos = var.indexOf(PREFIX_SEPARATOR); 281 if (prefixPos >= 0) 282 { 283 String prefix = var.substring(0, prefixPos); 284 String name = var.substring(prefixPos + 1); 285 String value = fetchLookupForPrefix(prefix).lookup(name); 286 if (value != null) 287 { 288 return value; 289 } 290 } 291 return fetchNoPrefixLookup().lookup(var); 292 } 293 294 /** 295 * Returns the lookup object to be used for variables without a prefix. This 296 * implementation will check whether a default lookup object was set. If 297 * this is the case, it will be returned. Otherwise a <b>null</b> lookup 298 * object will be returned. 299 * 300 * @return the lookup object to be used for variables without a prefix 301 */ 302 protected StrLookup fetchNoPrefixLookup() 303 { 304 return (getDefaultLookup() != null) ? getDefaultLookup() : StrLookup.noneLookup(); 305 } 306 307 /** 308 * Obtains the lookup object for the specified prefix. This method is called 309 * by the <code>lookup()</code> method. This implementation will check 310 * whether a lookup object is registered for the given prefix. If not, a 311 * <b>null</b> lookup object will be returned. 312 * 313 * @param prefix the prefix 314 * @return the lookup object to be used for this prefix 315 */ 316 protected StrLookup fetchLookupForPrefix(String prefix) 317 { 318 StrLookup lookup = (StrLookup) localLookups.get(prefix); 319 if (lookup == null) 320 { 321 lookup = StrLookup.noneLookup(); 322 } 323 return lookup; 324 } 325 326 // static initializer, sets up the map with the standard lookups 327 static 328 { 329 globalLookups = new HashMap(); 330 globalLookups.put(PREFIX_SYSPROPERTIES, StrLookup.systemPropertiesLookup()); 331 globalLookups.put(PREFIX_CONSTANTS, new ConstantLookup()); 332 } 333 }