/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.configuration.interpol;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.text.StrLookup;

A class that handles interpolation (variable substitution) for configuration objects.

Each instance of AbstractConfiguration is associated with an object of this class. All interpolation tasks are delegated to this object.

ConfigurationInterpolator works together with the StrSubstitutor class from Commons Lang. By extending StrLookup it is able to provide values for variables that appear in expressions.

The basic idea of this class is that it can maintain a set of primitive StrLookup objects, each of which is identified by a special prefix. The variables to be processed have the form ${prefix:name}. ConfigurationInterpolator will extract the prefix and determine, which primitive lookup object is registered for it. Then the name of the variable is passed to this object to obtain the actual value. It is also possible to define a default lookup object, which will be used for variables that do not have a prefix or that cannot be resolved by their associated lookup object.

When a new instance of this class is created it is initialized with a default set of primitive lookup objects. This set can be customized using the static methods registerGlobalLookup() and deregisterGlobalLookup(). Per default it contains the following standard lookup objects:

Prefix Lookup object
sys With this prefix a lookup object is associated that is able to resolve system properties.
const The const prefix indicates that a variable is to be interpreted as a constant member field of a class (i.e. a field with the static final modifiers). The name of the variable must be of the form <full qualified class name>.<field name>, e.g. org.apache.commons.configuration.interpol.ConfigurationInterpolator.PREFIX_CONSTANTS.

After an instance has been created the current set of lookup objects can be modified using the registerLookup() and deregisterLookup() methods. The default lookup object (that is invoked for variables without a prefix) can be set with the setDefaultLookup() method. (If a ConfigurationInterpolator instance is created by a configuration object, this lookup points to the configuration itself, so that variables are resolved using the configuration's properties. This ensures backward compatibility to earlier version of Commons Configuration.)

Implementation node: Instances of this class are not thread-safe related to modifications of their current set of registered lookup objects. It is intended that each instance is associated with a single Configuration object and used for its interpolation tasks.

Author:Commons Configuration team
Version:$Id: ConfigurationInterpolator.java 1295276 2012-02-29 21:11:35Z oheger $
Since:1.4
/** * <p> * A class that handles interpolation (variable substitution) for configuration * objects. * </p> * <p> * Each instance of {@code AbstractConfiguration} is associated with an * object of this class. All interpolation tasks are delegated to this object. * </p> * <p> * {@code ConfigurationInterpolator} works together with the * {@code StrSubstitutor} class from <a * href="http://commons.apache.org/lang">Commons Lang</a>. By extending * {@code StrLookup} it is able to provide values for variables that * appear in expressions. * </p> * <p> * The basic idea of this class is that it can maintain a set of primitive * {@code StrLookup} objects, each of which is identified by a special * prefix. The variables to be processed have the form * <code>${prefix:name}</code>. {@code ConfigurationInterpolator} will * extract the prefix and determine, which primitive lookup object is registered * for it. Then the name of the variable is passed to this object to obtain the * actual value. It is also possible to define a default lookup object, which * will be used for variables that do not have a prefix or that cannot be * resolved by their associated lookup object. * </p> * <p> * When a new instance of this class is created it is initialized with a default * set of primitive lookup objects. This set can be customized using the static * methods {@code registerGlobalLookup()} and * {@code deregisterGlobalLookup()}. Per default it contains the * following standard lookup objects: * </p> * <p> * <table border="1"> * <tr> * <th>Prefix</th> * <th>Lookup object</th> * </tr> * <tr> * <td valign="top">sys</td> * <td>With this prefix a lookup object is associated that is able to resolve * system properties.</td> * </tr> * <tr> * <td valign="top">const</td> * <td>The {@code const} prefix indicates that a variable is to be * interpreted as a constant member field of a class (i.e. a field with the * <b>static final</b> modifiers). The name of the variable must be of the form * {@code <full qualified class name>.<field name>}, e.g. * {@code org.apache.commons.configuration.interpol.ConfigurationInterpolator.PREFIX_CONSTANTS}. * </td> * </tr> * </table> * </p> * <p> * After an instance has been created the current set of lookup objects can be * modified using the {@code registerLookup()} and * {@code deregisterLookup()} methods. The default lookup object (that is * invoked for variables without a prefix) can be set with the * {@code setDefaultLookup()} method. (If a * {@code ConfigurationInterpolator} instance is created by a * configuration object, this lookup points to the configuration itself, so that * variables are resolved using the configuration's properties. This ensures * backward compatibility to earlier version of Commons Configuration.) * </p> * <p> * Implementation node: Instances of this class are not thread-safe related to * modifications of their current set of registered lookup objects. It is * intended that each instance is associated with a single * {@code Configuration} object and used for its interpolation tasks. * </p> * * @version $Id: ConfigurationInterpolator.java 1295276 2012-02-29 21:11:35Z oheger $ * @since 1.4 * @author <a * href="http://commons.apache.org/configuration/team-list.html">Commons * Configuration team</a> */
public class ConfigurationInterpolator extends StrLookup {
Constant for the prefix of the standard lookup object for resolving system properties.
/** * Constant for the prefix of the standard lookup object for resolving * system properties. */
public static final String PREFIX_SYSPROPERTIES = "sys";
Constant for the prefix of the standard lookup object for resolving constant values.
/** * Constant for the prefix of the standard lookup object for resolving * constant values. */
public static final String PREFIX_CONSTANTS = "const";
Constant for the prefix of the standard lookup object for resolving environment properties.
Since:1.7
/** * Constant for the prefix of the standard lookup object for resolving * environment properties. * @since 1.7 */
public static final String PREFIX_ENVIRONMENT = "env";
Constant for the prefix separator.
/** Constant for the prefix separator. */
private static final char PREFIX_SEPARATOR = ':';
A map with the globally registered lookup objects.
/** A map with the globally registered lookup objects. */
private static Map<String, StrLookup> globalLookups;
A map with the locally registered lookup objects.
/** A map with the locally registered lookup objects. */
private Map<String, StrLookup> localLookups;
Stores the default lookup object.
/** Stores the default lookup object. */
private StrLookup defaultLookup;
Stores a parent interpolator objects if the interpolator is nested hierarchically.
/** Stores a parent interpolator objects if the interpolator is nested hierarchically. */
private ConfigurationInterpolator parentInterpolator;
Creates a new instance of ConfigurationInterpolator.
/** * Creates a new instance of {@code ConfigurationInterpolator}. */
public ConfigurationInterpolator() { synchronized (globalLookups) { localLookups = new HashMap<String, StrLookup>(globalLookups); } }
Registers the given lookup object for the specified prefix globally. This means that all instances that are created later will use this lookup object for this prefix. If for this prefix a lookup object is already registered, the new lookup object will replace the old one. Note that the lookup objects registered here will be shared between multiple clients. So they should be thread-safe.
Params:
  • prefix – the variable prefix (must not be null)
  • lookup – the lookup object to be used for this prefix (must not be null)
/** * Registers the given lookup object for the specified prefix globally. This * means that all instances that are created later will use this lookup * object for this prefix. If for this prefix a lookup object is already * registered, the new lookup object will replace the old one. Note that the * lookup objects registered here will be shared between multiple clients. * So they should be thread-safe. * * @param prefix the variable prefix (must not be <b>null</b>) * @param lookup the lookup object to be used for this prefix (must not be * <b>null</b>) */
public static void registerGlobalLookup(String prefix, StrLookup lookup) { if (prefix == null) { throw new IllegalArgumentException( "Prefix for lookup object must not be null!"); } if (lookup == null) { throw new IllegalArgumentException( "Lookup object must not be null!"); } synchronized (globalLookups) { globalLookups.put(prefix, lookup); } }
Deregisters the global lookup object for the specified prefix. This means that this lookup object won't be available for later created instances any more. For already existing instances this operation does not have any impact.
Params:
  • prefix – the variable prefix
Returns:a flag whether for this prefix a lookup object had been registered
/** * Deregisters the global lookup object for the specified prefix. This means * that this lookup object won't be available for later created instances * any more. For already existing instances this operation does not have any * impact. * * @param prefix the variable prefix * @return a flag whether for this prefix a lookup object had been * registered */
public static boolean deregisterGlobalLookup(String prefix) { synchronized (globalLookups) { return globalLookups.remove(prefix) != null; } }
Registers the given lookup object for the specified prefix at this instance. From now on this lookup object will be used for variables that have the specified prefix.
Params:
  • prefix – the variable prefix (must not be null)
  • lookup – the lookup object to be used for this prefix (must not be null)
/** * Registers the given lookup object for the specified prefix at this * instance. From now on this lookup object will be used for variables that * have the specified prefix. * * @param prefix the variable prefix (must not be <b>null</b>) * @param lookup the lookup object to be used for this prefix (must not be * <b>null</b>) */
public void registerLookup(String prefix, StrLookup lookup) { if (prefix == null) { throw new IllegalArgumentException( "Prefix for lookup object must not be null!"); } if (lookup == null) { throw new IllegalArgumentException( "Lookup object must not be null!"); } localLookups.put(prefix, lookup); }
Deregisters the lookup object for the specified prefix at this instance. It will be removed from this instance.
Params:
  • prefix – the variable prefix
Returns:a flag whether for this prefix a lookup object had been registered
/** * Deregisters the lookup object for the specified prefix at this instance. * It will be removed from this instance. * * @param prefix the variable prefix * @return a flag whether for this prefix a lookup object had been * registered */
public boolean deregisterLookup(String prefix) { return localLookups.remove(prefix) != null; }
Returns a set with the prefixes, for which lookup objects are registered at this instance. This means that variables with these prefixes can be processed.
Returns:a set with the registered variable prefixes
/** * Returns a set with the prefixes, for which lookup objects are registered * at this instance. This means that variables with these prefixes can be * processed. * * @return a set with the registered variable prefixes */
public Set<String> prefixSet() { return localLookups.keySet(); }
Returns the default lookup object.
Returns:the default lookup object
/** * Returns the default lookup object. * * @return the default lookup object */
public StrLookup getDefaultLookup() { return defaultLookup; }
Sets the default lookup object. This lookup object will be used for all variables without a special prefix. If it is set to null, such variables won't be processed.
Params:
  • defaultLookup – the new default lookup object
/** * Sets the default lookup object. This lookup object will be used for all * variables without a special prefix. If it is set to <b>null</b>, such * variables won't be processed. * * @param defaultLookup the new default lookup object */
public void setDefaultLookup(StrLookup defaultLookup) { this.defaultLookup = defaultLookup; }
Resolves the specified variable. This implementation will try to extract a variable prefix from the given variable name (the first colon (':') is used as prefix separator). It then passes the name of the variable with the prefix stripped to the lookup object registered for this prefix. If no prefix can be found or if the associated lookup object cannot resolve this variable, the default lookup object will be used.
Params:
  • var – the name of the variable whose value is to be looked up
Returns:the value of this variable or null if it cannot be resolved
/** * Resolves the specified variable. This implementation will try to extract * a variable prefix from the given variable name (the first colon (':') is * used as prefix separator). It then passes the name of the variable with * the prefix stripped to the lookup object registered for this prefix. If * no prefix can be found or if the associated lookup object cannot resolve * this variable, the default lookup object will be used. * * @param var the name of the variable whose value is to be looked up * @return the value of this variable or <b>null</b> if it cannot be * resolved */
@Override public String lookup(String var) { if (var == null) { return null; } int prefixPos = var.indexOf(PREFIX_SEPARATOR); if (prefixPos >= 0) { String prefix = var.substring(0, prefixPos); String name = var.substring(prefixPos + 1); String value = fetchLookupForPrefix(prefix).lookup(name); if (value == null && getParentInterpolator() != null) { value = getParentInterpolator().lookup(name); } if (value != null) { return value; } } String value = fetchNoPrefixLookup().lookup(var); if (value == null && getParentInterpolator() != null) { value = getParentInterpolator().lookup(var); } return value; }
Returns the lookup object to be used for variables without a prefix. This implementation will check whether a default lookup object was set. If this is the case, it will be returned. Otherwise a null lookup object will be returned (never null).
Returns:the lookup object to be used for variables without a prefix
/** * Returns the lookup object to be used for variables without a prefix. This * implementation will check whether a default lookup object was set. If * this is the case, it will be returned. Otherwise a <b>null</b> lookup * object will be returned (never <b>null</b>). * * @return the lookup object to be used for variables without a prefix */
protected StrLookup fetchNoPrefixLookup() { return (getDefaultLookup() != null) ? getDefaultLookup() : StrLookup.noneLookup(); }
Obtains the lookup object for the specified prefix. This method is called by the lookup() method. This implementation will check whether a lookup object is registered for the given prefix. If not, a null lookup object will be returned (never null).
Params:
  • prefix – the prefix
Returns:the lookup object to be used for this prefix
/** * Obtains the lookup object for the specified prefix. This method is called * by the {@code lookup()} method. This implementation will check * whether a lookup object is registered for the given prefix. If not, a * <b>null</b> lookup object will be returned (never <b>null</b>). * * @param prefix the prefix * @return the lookup object to be used for this prefix */
protected StrLookup fetchLookupForPrefix(String prefix) { StrLookup lookup = localLookups.get(prefix); if (lookup == null) { lookup = StrLookup.noneLookup(); } return lookup; }
Registers the local lookup instances for the given interpolator.
Params:
  • interpolator – the instance receiving the local lookups
Since:upcoming
/** * Registers the local lookup instances for the given interpolator. * * @param interpolator the instance receiving the local lookups * @since upcoming */
public void registerLocalLookups(ConfigurationInterpolator interpolator) { interpolator.localLookups.putAll(localLookups); }
Sets the parent interpolator. This object is used if the interpolation is nested hierarchically and the current interpolation object cannot resolve a variable.
Params:
  • parentInterpolator – the parent interpolator object or null
Since:upcoming
/** * Sets the parent interpolator. This object is used if the interpolation is nested * hierarchically and the current interpolation object cannot resolve a variable. * * @param parentInterpolator the parent interpolator object or <b>null</b> * @since upcoming */
public void setParentInterpolator(ConfigurationInterpolator parentInterpolator) { this.parentInterpolator = parentInterpolator; }
Requests the parent interpolator. This object is used if the interpolation is nested hierarchically and the current interpolation
Returns:the parent interpolator or null
Since:upcoming
/** * Requests the parent interpolator. This object is used if the interpolation is nested * hierarchically and the current interpolation * * @return the parent interpolator or <b>null</b> * @since upcoming */
public ConfigurationInterpolator getParentInterpolator() { return this.parentInterpolator; } // static initializer, sets up the map with the standard lookups static { globalLookups = new HashMap<String, StrLookup>(); globalLookups.put(PREFIX_SYSPROPERTIES, StrLookup.systemPropertiesLookup()); globalLookups.put(PREFIX_CONSTANTS, new ConstantLookup()); globalLookups.put(PREFIX_ENVIRONMENT, new EnvironmentLookup()); } }