View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration.interpol;
18  
19  import java.util.HashMap;
20  import java.util.Map;
21  import java.util.Set;
22  
23  import org.apache.commons.lang.text.StrLookup;
24  
25  /***
26   * <p>
27   * A class that handles interpolation (variable substitution) for configuration
28   * objects.
29   * </p>
30   * <p>
31   * Each instance of <code>AbstractConfiguration</code> is associated with an
32   * object of this class. All interpolation tasks are delegated to this object.
33   * </p>
34   * <p>
35   * <code>ConfigurationInterpolator</code> works together with the
36   * <code>StrSubstitutor</code> class from <a
37   * href="http://commons.apache.org/lang">Commons Lang</a>. By extending
38   * <code>StrLookup</code> it is able to provide values for variables that
39   * appear in expressions.
40   * </p>
41   * <p>
42   * The basic idea of this class is that it can maintain a set of primitive
43   * <code>StrLookup</code> objects, each of which is identified by a special
44   * prefix. The variables to be processed have the form
45   * <code>${prefix:name}</code>. <code>ConfigurationInterpolator</code> will
46   * extract the prefix and determine, which primitive lookup object is registered
47   * for it. Then the name of the variable is passed to this object to obtain the
48   * actual value. It is also possible to define a default lookup object, which
49   * will be used for variables that do not have a prefix or that cannot be
50   * resolved by their associated lookup object.
51   * </p>
52   * <p>
53   * When a new instance of this class is created it is initialized with a default
54   * set of primitive lookup objects. This set can be customized using the static
55   * methods <code>registerGlobalLookup()</code> and
56   * <code>deregisterGlobalLookup()</code>. Per default it contains the
57   * following standard lookup objects:
58   * </p>
59   * <p>
60   * <table border="1">
61   * <tr>
62   * <th>Prefix</th>
63   * <th>Lookup object</th>
64   * </tr>
65   * <tr>
66   * <td valign="top">sys</td>
67   * <td>With this prefix a lookup object is associated that is able to resolve
68   * system properties.</td>
69   * </tr>
70   * <tr>
71   * <td valign="top">const</td>
72   * <td>The <code>const</code> prefix indicates that a variable is to be
73   * interpreted as a constant member field of a class (i.e. a field with the
74   * <b>static final</b> modifiers). The name of the variable must be of the form
75   * <code>&lt;full qualified class name&gt;.&lt;field name&gt;</code>, e.g.
76   * <code>org.apache.commons.configuration.interpol.ConfigurationInterpolator.PREFIX_CONSTANTS
77   * </code>.</td>
78   * </tr>
79   * </table>
80   * </p>
81   * <p>
82   * After an instance has been created the current set of lookup objects can be
83   * modified using the <code>registerLookup()</code> and
84   * <code>deregisterLookup()</code> methods. The default lookup object (that is
85   * invoked for variables without a prefix) can be set with the
86   * <code>setDefaultLookup()</code> method. (If a
87   * <code>ConfigurationInterpolator</code> instance is created by a
88   * configuration object, this lookup points to the configuration itself, so that
89   * variables are resolved using the configuration's properties. This ensures
90   * backward compatibility to earlier version of Commons Configuration.)
91   * </p>
92   * <p>
93   * Implementation node: Instances of this class are not thread-safe related to
94   * modifications of their current set of registered lookup objects. It is
95   * intended that each instance is associated with a single
96   * <code>Configuration</code> object and used for its interpolation tasks.
97   * </p>
98   *
99   * @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 }