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. Each 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://jakarta.apache.org/commons/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 are 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.
050: * </p>
051: * <p>
052: * When a new instance of this class is created it is initialized with a default
053: * set of primitive lookup objects. This set can be customized using the static
054: * methods <code>registerGlobalLookup()</code> and
055: * <code>deregisterGlobalLookup()</code>. Per default it contains the
056: * following standard lookup objects:
057: * </p>
058: * <p>
059: * <table border="1">
060: * <tr>
061: * <th>Prefix</th>
062: * <th>Lookup object</th>
063: * </tr>
064: * <tr>
065: * <td valign="top">sys</td>
066: * <td>With this prefix a lookup object is associated that is able to resolve
067: * system properties.</td>
068: * </tr>
069: * <tr>
070: * <td valign="top">const</td>
071: * <td>The <code>const</code> prefix indicates that a variable is to be
072: * interpreted as a constant member field of a class (i.e. a field with the
073: * <b>static final</b> modifiers). The name of the variable must be of the form
074: * <code><full qualified class name>.<field name></code>, e.g.
075: * <code>org.apache.commons.configuration.interpol.ConfigurationInterpolator.PREFIX_CONSTANTS
076: * </code>.</td>
077: * </tr>
078: * </table>
079: * </p>
080: * <p>
081: * After an instance has been created the current set of lookup objects can be
082: * modified using the <code>registerLookup()</code> and
083: * <code>deregisterLookup()</code> methods. The default lookup object (that is
084: * invoked for variables without a prefix) can be set with the
085: * <code>setDefaultLookup()</code> method. (If a
086: * <code>ConfigurationInterpolator</code> instance is created by a
087: * configuration object, this lookup points to the configuration itself, so that
088: * variables are resolved using the configuration's properties. This ensures
089: * backward compatibility to earlier version of Commons Configuration.)
090: * </p>
091: * <p>
092: * Implementation node: Instances of this class are not thread-safe related to
093: * modifications of their current set of registered lookup objects. It is
094: * intended that each instance is associated with a single
095: * <code>Configuration</conde>
096: * object and used for its interpolation tasks.</p>
097: *
098: * @version $Id: ConfigurationInterpolator.java 491243 2006-12-30 16:04:03Z oheger $
099: * @since 1.4
100: * @author <a
101: * href="http://jakarta.apache.org/commons/configuration/team-list.html">Commons
102: * Configuration team</a>
103: */
104: public class ConfigurationInterpolator extends StrLookup {
105: /**
106: * Constant for the prefix of the standard lookup object for resolving
107: * system properties.
108: */
109: public static final String PREFIX_SYSPROPERTIES = "sys";
110:
111: /**
112: * Constant for the prefix of the standard lookup object for resolving
113: * constant values.
114: */
115: public static final String PREFIX_CONSTANTS = "const";
116:
117: /** Constant for the prefix separator. */
118: private static final char PREFIX_SEPARATOR = ':';
119:
120: /** A map with the globally registered lookup objects. */
121: private static Map globalLookups;
122:
123: /** A map with the locally registered lookup objects. */
124: private Map localLookups;
125:
126: /** Stores the default lookup object. */
127: private StrLookup defaultLookup;
128:
129: /**
130: * Creates a new instance of <code>ConfigurationInterpolator</code>.
131: */
132: public ConfigurationInterpolator() {
133: synchronized (globalLookups) {
134: localLookups = new HashMap(globalLookups);
135: }
136: }
137:
138: /**
139: * Registers the given lookup object for the specified prefix globally. This
140: * means that all instances that are created later will use this lookup
141: * object for this prefix. If for this prefix a lookup object is already
142: * registered, the new lookup object will replace the old one. Note that the
143: * lookup objects registered here will be shared between multiple clients.
144: * So they should be thread-safe.
145: *
146: * @param prefix the variable prefix (must not be <b>null</b>)
147: * @param lookup the lookup object to be used for this prefix (must not be
148: * <b>null</b>)
149: */
150: public static void registerGlobalLookup(String prefix,
151: StrLookup lookup) {
152: if (prefix == null) {
153: throw new IllegalArgumentException(
154: "Prefix for lookup object must not be null!");
155: }
156: if (lookup == null) {
157: throw new IllegalArgumentException(
158: "Lookup object must not be null!");
159: }
160: synchronized (globalLookups) {
161: globalLookups.put(prefix, lookup);
162: }
163: }
164:
165: /**
166: * Deregisters the global lookup object for the specified prefix. This means
167: * that this lookup object won't be available for later created instances
168: * any more. For already existing instances this operation does not have any
169: * impact.
170: *
171: * @param prefix the variable prefix
172: * @return a flag whether for this prefix a lookup object had been
173: * registered
174: */
175: public static boolean deregisterGlobalLookup(String prefix) {
176: synchronized (globalLookups) {
177: return globalLookups.remove(prefix) != null;
178: }
179: }
180:
181: /**
182: * Registers the given lookup object for the specified prefix at this
183: * instance. From now on this lookup object will be used for variables that
184: * have the specified prefix.
185: *
186: * @param prefix the variable prefix (must not be <b>null</b>)
187: * @param lookup the lookup object to be used for this prefix (must not be
188: * <b>null</b>)
189: */
190: public void registerLookup(String prefix, StrLookup lookup) {
191: if (prefix == null) {
192: throw new IllegalArgumentException(
193: "Prefix for lookup object must not be null!");
194: }
195: if (lookup == null) {
196: throw new IllegalArgumentException(
197: "Lookup object must not be null!");
198: }
199: localLookups.put(prefix, lookup);
200: }
201:
202: /**
203: * Deregisters the lookup object for the specified prefix at this instance.
204: * It will be removed from this instance.
205: *
206: * @param prefix the variable prefix
207: * @return a flag whether for this prefix a lookup object had been
208: * registered
209: */
210: public boolean deregisterLookup(String prefix) {
211: return localLookups.remove(prefix) != null;
212: }
213:
214: /**
215: * Returns a set with the prefixes, for which lookup objects are registered
216: * at this instance. This means that variables with these prefixes can be
217: * processed.
218: *
219: * @return a set with the registered variable prefixes
220: */
221: public Set prefixSet() {
222: return localLookups.keySet();
223: }
224:
225: /**
226: * Returns the default lookup object.
227: *
228: * @return the default lookup object
229: */
230: public StrLookup getDefaultLookup() {
231: return defaultLookup;
232: }
233:
234: /**
235: * Sets the default lookup object. This lookup object will be used for all
236: * variables without a special prefix. If it is set to <b>null</b>, such
237: * variables won't be processed.
238: *
239: * @param defaultLookup the new default lookup object
240: */
241: public void setDefaultLookup(StrLookup defaultLookup) {
242: this .defaultLookup = defaultLookup;
243: }
244:
245: /**
246: * Resolves the specified variable. This implementation will try to extract
247: * a variable prefix from the given variable name (the first colon (':') is
248: * used as prefix separator). It then passes the name of the variable with
249: * the prefix stripped to the lookup object registered for this prefix. If
250: * no prefix can be found, the default lookup object will be used.
251: *
252: * @param var the name of the variable whose value is to be looked up
253: * @return the value of this variable or <b>null</b> if it cannot be
254: * resolved
255: */
256: public String lookup(String var) {
257: if (var == null) {
258: return null;
259: }
260:
261: int prefixPos = var.indexOf(PREFIX_SEPARATOR);
262: if (prefixPos < 0) {
263: return fetchNoPrefixLookup().lookup(var);
264: } else {
265: String prefix = var.substring(0, prefixPos);
266: String name = var.substring(prefixPos + 1);
267: return fetchLookupForPrefix(prefix).lookup(name);
268: }
269: }
270:
271: /**
272: * Returns the lookup object to be used for variables without a prefix. This
273: * implementation will check whether a default lookup object was set. If
274: * this is the case, it will be returned. Otherwise a <b>null</b> lookup
275: * object will be returned.
276: *
277: * @return the lookup object to be used for variables without a prefix
278: */
279: protected StrLookup fetchNoPrefixLookup() {
280: return (getDefaultLookup() != null) ? getDefaultLookup()
281: : StrLookup.noneLookup();
282: }
283:
284: /**
285: * Obtains the lookup object for the specified prefix. This method is called
286: * by the <code>lookup()</code> method. This implementation will check
287: * whether a lookup object is registered for the given prefix. If not, a
288: * <b>null</b> lookup object will be returned.
289: *
290: * @param prefix the prefix
291: * @return the lookup object to be used for this prefix
292: */
293: protected StrLookup fetchLookupForPrefix(String prefix) {
294: StrLookup lookup = (StrLookup) localLookups.get(prefix);
295: if (lookup == null) {
296: lookup = StrLookup.noneLookup();
297: }
298: return lookup;
299: }
300:
301: // static initializer, sets up the map with the standard lookups
302: static {
303: globalLookups = new HashMap();
304: globalLookups.put(PREFIX_SYSPROPERTIES, StrLookup
305: .systemPropertiesLookup());
306: globalLookups.put(PREFIX_CONSTANTS, new ConstantLookup());
307: }
308: }
|