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.ArrayList;
20  
21  import org.apache.commons.configuration.AbstractConfiguration;
22  import org.apache.commons.configuration.ConfigurationRuntimeException;
23  import org.apache.commons.jexl2.Expression;
24  import org.apache.commons.jexl2.JexlContext;
25  import org.apache.commons.jexl2.JexlEngine;
26  import org.apache.commons.jexl2.MapContext;
27  import org.apache.commons.lang.ClassUtils;
28  import org.apache.commons.lang.StringUtils;
29  import org.apache.commons.lang.text.StrLookup;
30  import org.apache.commons.lang.text.StrSubstitutor;
31  
32  /**
33   * Lookup that allows expressions to be evaluated.
34   *
35   * <pre>
36   *     ExprLookup.Variables vars = new ExprLookup.Variables();
37   *     vars.add(new ExprLookup.Variable("String", org.apache.commons.lang.StringUtils.class));
38   *     vars.add(new ExprLookup.Variable("Util", new Utility("Hello")));
39   *     vars.add(new ExprLookup.Variable("System", "Class:java.lang.System"));
40   *     XMLConfiguration config = new XMLConfiguration(TEST_FILE);
41   *     config.setLogger(log);
42   *     ExprLookup lookup = new ExprLookup(vars);
43   *     lookup.setConfiguration(config);
44   *     String str = lookup.lookup("'$[element] ' + String.trimToEmpty('$[space.description]')");
45   * </pre>
46   *
47   * In the example above TEST_FILE contains xml that looks like:
48   * <pre>
49   * &lt;configuration&gt;
50   *   &lt;element&gt;value&lt;/element&gt;
51   *   &lt;space xml:space="preserve"&gt;
52   *     &lt;description xml:space="default"&gt;     Some text      &lt;/description&gt;
53   *   &lt;/space&gt;
54   * &lt;/configuration&gt;
55   * </pre>
56   *
57   * The result will be "value Some text".
58   *
59   * This lookup uses Apache Commons Jexl and requires that the dependency be added to any
60   * projects which use this.
61   *
62   * @since 1.7
63   * @author <a
64   * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
65   * @version $Id: ExprLookup.java 1234539 2012-01-22 16:19:15Z oheger $
66   */
67  public class ExprLookup extends StrLookup
68  {
69      /** Prefix to identify a Java Class object */
70      private static final String CLASS = "Class:";
71  
72      /** The default prefix for subordinate lookup expressions */
73      private static final String DEFAULT_PREFIX = "$[";
74  
75      /** The default suffix for subordinate lookup expressions */
76      private static final String DEFAULT_SUFFIX = "]";
77  
78      /** Configuration being operated on */
79      private AbstractConfiguration configuration;
80  
81      /** The engine. */
82      private final JexlEngine engine = new JexlEngine();
83  
84      /** The variables maintained by this object. */
85      private Variables variables;
86  
87      /** The String to use to start subordinate lookup expressions */
88      private String prefixMatcher = DEFAULT_PREFIX;
89  
90      /** The String to use to terminate subordinate lookup expressions */
91      private String suffixMatcher = DEFAULT_SUFFIX;
92  
93      /**
94       * The default constructor. Will get used when the Lookup is constructed via
95       * configuration.
96       */
97      public ExprLookup()
98      {
99      }
100 
101     /**
102      * Constructor for use by applications.
103      * @param list The list of objects to be accessible in expressions.
104      */
105     public ExprLookup(Variables list)
106     {
107         setVariables(list);
108     }
109 
110     /**
111      * Constructor for use by applications.
112      * @param list The list of objects to be accessible in expressions.
113      * @param prefix The prefix to use for subordinate lookups.
114      * @param suffix The suffix to use for subordinate lookups.
115      */
116     public ExprLookup(Variables list, String prefix, String suffix)
117     {
118         this(list);
119         setVariablePrefixMatcher(prefix);
120         setVariableSuffixMatcher(suffix);
121     }
122 
123     /**
124      * Set the prefix to use to identify subordinate expressions. This cannot be the
125      * same as the prefix used for the primary expression.
126      * @param prefix The String identifying the beginning of the expression.
127      */
128     public void setVariablePrefixMatcher(String prefix)
129     {
130         prefixMatcher = prefix;
131     }
132 
133 
134     /**
135      * Set the suffix to use to identify subordinate expressions. This cannot be the
136      * same as the suffix used for the primary expression.
137      * @param suffix The String identifying the end of the expression.
138      */
139     public void setVariableSuffixMatcher(String suffix)
140     {
141         suffixMatcher = suffix;
142     }
143 
144     /**
145      * Add the Variables that will be accessible within expressions.
146      * @param list The list of Variables.
147      */
148     public void setVariables(Variables list)
149     {
150         variables = new Variables(list);
151     }
152 
153     /**
154      * Returns the list of Variables that are accessible within expressions.
155      * @return the List of Variables that are accessible within expressions.
156      */
157     public Variables getVariables()
158     {
159         return null;
160     }
161 
162     /**
163      * Set the configuration to be used to interpolate subordinate expressions.
164      * @param config The Configuration.
165      */
166     public void setConfiguration(AbstractConfiguration config)
167     {
168         this.configuration = config;
169     }
170 
171     /**
172      * Evaluates the expression.
173      * @param var The expression.
174      * @return The String result of the expression.
175      */
176     @Override
177     public String lookup(String var)
178     {
179         ConfigurationInterpolator interp = configuration.getInterpolator();
180         StrSubstitutor subst = new StrSubstitutor(interp, prefixMatcher, suffixMatcher,
181                 StrSubstitutor.DEFAULT_ESCAPE);
182 
183         String result = subst.replace(var);
184 
185         try
186         {
187             Expression exp = engine.createExpression(result);
188             result = (String) exp.evaluate(createContext());
189         }
190         catch (Exception e)
191         {
192             configuration.getLogger().debug("Error encountered evaluating " + result, e);
193         }
194 
195         return result;
196     }
197 
198     /**
199      * Creates a new {@code JexlContext} and initializes it with the variables
200      * managed by this Lookup object.
201      *
202      * @return the newly created context
203      */
204     private JexlContext createContext()
205     {
206         JexlContext ctx = new MapContext();
207         initializeContext(ctx);
208         return ctx;
209     }
210 
211     /**
212      * Initializes the specified context with the variables managed by this
213      * Lookup object.
214      *
215      * @param ctx the context to be initialized
216      */
217     private void initializeContext(JexlContext ctx)
218     {
219         for (Variable var : variables)
220         {
221             ctx.set(var.getName(), var.getValue());
222         }
223     }
224 
225     /**
226      * List wrapper used to allow the Variables list to be created as beans in
227      * DefaultConfigurationBuilder.
228      */
229     public static class Variables extends ArrayList<Variable>
230     {
231         /**
232          * The serial version UID.
233          */
234         private static final long serialVersionUID = 20111205L;
235 
236         /**
237          * Creates a new empty instance of {@code Variables}.
238          */
239         public Variables()
240         {
241             super();
242         }
243 
244         /**
245          * Creates a new instance of {@code Variables} and copies the content of
246          * the given object.
247          *
248          * @param vars the {@code Variables} object to be copied
249          */
250         public Variables(Variables vars)
251         {
252             super(vars);
253         }
254 
255         public Variable getVariable()
256         {
257             if (size() > 0)
258             {
259                 return get(size() - 1);
260             }
261             else
262             {
263                 return null;
264             }
265         }
266 
267     }
268 
269     /**
270      * The key and corresponding object that will be made available to the
271      * JexlContext for use in expressions.
272      */
273     public static class Variable
274     {
275         /** The name to be used in expressions */
276         private String key;
277 
278         /** The object to be accessed in expressions */
279         private Object value;
280 
281         public Variable()
282         {
283         }
284 
285         public Variable(String name, Object value)
286         {
287             setName(name);
288             setValue(value);
289         }
290 
291         public String getName()
292         {
293             return key;
294         }
295 
296         public void setName(String name)
297         {
298             this.key = name;
299         }
300 
301         public Object getValue()
302         {
303             return value;
304         }
305 
306         public void setValue(Object value) throws ConfigurationRuntimeException
307         {
308             try
309             {
310                 if (!(value instanceof String))
311                 {
312                     this.value = value;
313                     return;
314                 }
315                 String val = (String) value;
316                 String name = StringUtils.removeStartIgnoreCase(val, CLASS);
317                 Class<?> clazz = ClassUtils.getClass(name);
318                 if (name.length() == val.length())
319                 {
320                     this.value = clazz.newInstance();
321                 }
322                 else
323                 {
324                     this.value = clazz;
325                 }
326             }
327             catch (Exception e)
328             {
329                 throw new ConfigurationRuntimeException("Unable to create " + value, e);
330             }
331 
332         }
333     }
334 }