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>&lt;full qualified class name&gt;.&lt;field name&gt;</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    }