View Javadoc

1   /*
2    * $Id: ExpressionEvaluatorManager.java 12100 2008-06-19 08:58:48Z rossmason $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  package org.mule.util.expression;
11  
12  import org.mule.api.lifecycle.Disposable;
13  import org.mule.config.i18n.CoreMessages;
14  import org.mule.util.TemplateParser;
15  
16  import java.util.Iterator;
17  
18  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
19  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
20  
21  /**
22   * Provides universal access for evaluating expressions embedded in Mule configurations, such  as Xml, Java,
23   * scripting and annotations.
24   *
25   * Users can register or unregister {@link ExpressionEvaluator} through this interface.
26   * */
27  public class ExpressionEvaluatorManager
28  {
29  
30      public static final String DEFAULT_EXPRESSION_PREFIX = "${";
31  
32      private static TemplateParser parser = TemplateParser.createAntStyleParser();
33  
34      private static ConcurrentMap evaluators = new ConcurrentHashMap(8);
35  
36      public static void registerEvaluator(ExpressionEvaluator extractor)
37      {
38          if (extractor == null)
39          {
40              throw new IllegalArgumentException(CoreMessages.objectIsNull("extractor").getMessage());
41          }
42          Object previous = evaluators.putIfAbsent(extractor.getName(), extractor);
43          if (previous != null)
44          {
45              throw new IllegalArgumentException(CoreMessages.objectAlreadyExists(extractor.getName()).getMessage());
46          }
47      }
48  
49      /**
50       * Checks whether an evaluator is registered with the manager
51       * @param name the name of the expression evaluator
52       * @return true if the evaluator is registered with the manager, false otherwise
53       */
54      public static boolean isEvaluatorRegistered(String name)
55      {
56          return evaluators.containsKey(name);
57      }
58  
59      /**
60       * Removes the evaluator with the given name
61       * @param name the name of the evaluator to remove
62       */
63      public static ExpressionEvaluator unregisterEvaluator(String name)
64      {
65          if (name==null)
66          {
67              return null;
68          }
69          
70          ExpressionEvaluator evaluator = (ExpressionEvaluator) ExpressionEvaluatorManager.evaluators.remove(name);
71          if (evaluator instanceof Disposable)
72          {
73              ((Disposable) evaluator).dispose();
74          }
75          return evaluator;
76      }
77  
78      /**
79       * Evaluates the given expression.  The expression should be a single expression definition with or without 
80       * enclosing braces. i.e. "mule:serviceName" and "${mule:serviceName}" are both valid. For situations where
81       * one or more expressions need to be parsed within a single text, the {@link #parse(String, Object, boolean)}
82       * method should be used since it will iterate through all expressions in a string.
83       *
84       * @param expression a single expression i.e. xpath://foo
85       * @param object The object (usually {@link org.mule.api.MuleMessage}) to evaluate the expression on.
86       * @return the result of the evaluation
87       * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
88       * 'failIfNull is set to true.
89       */
90      public static Object evaluate(String expression, Object object) throws ExpressionRuntimeException
91      {
92          return evaluate(expression, object, DEFAULT_EXPRESSION_PREFIX, false);
93      }
94  
95      /**
96       * Evaluates the given expression.  The expression should be a single expression definition with or without
97       * enclosing braces. i.e. "mule:serviceName" and "${mule:serviceName}" are both valid. For situations where
98       * one or more expressions need to be parsed within a single text, the {@link #parse(String, Object, boolean)}
99       * method should be used since it will iterate through all expressions in a string.
100      *
101      * @param expression a single expression i.e. xpath://foo
102      * @param object The object (usually {@link org.mule.api.MuleMessage}) to evaluate the expression on.
103      * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns
104      * null.
105      * @return the result of the evaluation
106      * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
107      * 'failIfNull is set to true.
108      */
109     public static Object evaluate(String expression, Object object, boolean failIfNull) throws ExpressionRuntimeException
110     {
111         return evaluate(expression, object, DEFAULT_EXPRESSION_PREFIX, failIfNull);
112     }
113 
114     /**
115      * Evaluates the given expression.  The expression should be a single expression definition with or without
116      * enclosing braces. i.e. "mule:serviceName" and "${mule:serviceName}" are both valid. For situations where
117      * one or more expressions need to be parsed within a single text, the {@link #parse(String, Object, boolean)}
118      * method should be used since it will iterate through all expressions in a string.
119      *
120      * @param expression a single expression i.e. xpath://foo
121      * @param evaluator the evaluator to use when executing the expression
122      * @param object The object (usually {@link org.mule.api.MuleMessage}) to evaluate the expression on.
123      * It is unlikely that users will want to change this execpt maybe to use "["  instead.
124      * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns
125      * null.
126      * @return the result of the evaluation
127      * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
128      * 'failIfNull is set to true.
129      */
130     public static Object evaluate(String expression, String evaluator, Object object, boolean failIfNull) throws ExpressionRuntimeException
131     {
132         ExpressionEvaluator extractor = (ExpressionEvaluator) evaluators.get(evaluator);
133         if (extractor == null)
134         {
135             throw new IllegalArgumentException(CoreMessages.expressionEvaluatorNotRegistered(evaluator).getMessage());
136         }
137         Object result = extractor.evaluate(expression, object);
138         if (result == null && failIfNull)
139         {
140             throw new ExpressionRuntimeException(CoreMessages.expressionEvaluatorReturnedNull(evaluator, expression));
141         }
142         return result;
143     }
144     /**
145      * Evaluates the given expression.  The expression should be a single expression definition with or without
146      * enclosing braces. i.e. "mule:serviceName" and "${mule:serviceName}" are both valid. For situations where
147      * one or more expressions need to be parsed within a single text, the {@link #parse(String, Object, boolean)}
148      * method should be used since it will iterate through all expressions in a string.
149      *
150      * @param expression a single expression i.e. xpath://foo
151      * @param object The object (usually {@link org.mule.api.MuleMessage}) to evaluate the expression on.
152      * @param expressionPrefix the expression prefix to use. The default is "${" but any character is valid.
153      * It is unlikely that users will want to change this except maybe to use "["  instead.
154      * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns
155      * null.
156      * @return the result of the evaluation
157      * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
158      * 'failIfNull is set to true.
159      */
160     public static Object evaluate(String expression, Object object, String expressionPrefix, boolean failIfNull) throws ExpressionRuntimeException
161     {
162         String name;
163 
164         if (expression == null)
165         {
166             throw new IllegalArgumentException(CoreMessages.objectIsNull("expression").getMessage());
167         }
168         if (expression.startsWith(expressionPrefix))
169         {
170             expression = expression.substring(2, expression.length()-1);
171         }
172         int i = expression.indexOf(":");
173         if (i >- 1)
174         {
175             name = expression.substring(0, i);
176             expression = expression.substring(i+1);
177         }
178         else
179         {
180             name = expression;
181             expression = null;
182         }
183         return evaluate(expression, name, object, failIfNull);
184 
185 
186     }
187 
188     /**
189      * Evaluates expressions in a given string. This method will iterate through each expression and evaluate it. If
190      * a user needs to evaluate a single expression they can use {@link #evaluate(String, Object, boolean)}.
191      *
192      * @param expression a single expression i.e. xpath://foo
193      * @param object The object (usually {@link org.mule.api.MuleMessage}) to evaluate the expression on.
194 
195      * @return the result of the evaluation
196      * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
197      * 'failIfNull is set to true.
198      */
199     public static String parse(String expression, Object object) throws ExpressionRuntimeException
200     {
201         return parse(expression, object, false);
202     }
203 
204     /**
205      * Evaluates expressions in a given string. This method will iterate through each expression and evaluate it. If
206      * a user needs to evaluate a single expression they can use {@link #evaluate(String, Object, boolean)}.
207      *
208      * @param expression a single expression i.e. xpath://foo
209      * @param object The object (usually {@link org.mule.api.MuleMessage}) to evaluate the expression on.
210      * @param failIfNull determines if an exception should be thrown if expression could not be evaluated or returns
211      * null.
212      * @return the result of the evaluation
213      * @throws ExpressionRuntimeException if the expression is invalid, or a null is found for the expression and
214      * 'failIfNull is set to true.
215      */
216     public static String parse(final String expression, final Object object, final boolean failIfNull) throws ExpressionRuntimeException
217     {
218         return parser.parse(new TemplateParser.TemplateCallback() {
219             public Object match(String token)
220             {
221                 return evaluate(token, object, failIfNull);
222             }
223         }, expression);
224     }
225 
226     /**
227      * Clears all registered evaluators from the manager.
228      */
229     public static synchronized void clearEvaluators()
230     {
231         for (Iterator iterator = evaluators.values().iterator(); iterator.hasNext();)
232         {
233             ExpressionEvaluator evaluator = (ExpressionEvaluator)iterator.next();
234             if(evaluator instanceof Disposable)
235             {
236                 ((Disposable) evaluator).dispose();
237             }
238         }
239         evaluators.clear();
240     }
241 
242     /**
243      * Determines if the expression is valid or not.  This only validates single expressions.
244      * @param expression the expression to validate
245      * @return true if the expression evaluator is recognised
246      */
247     public static boolean isValidExpression(String expression)
248     {
249         return isValidExpression(expression, DEFAULT_EXPRESSION_PREFIX);
250     }
251 
252     /**
253      * Determines if the expression is valid or not.  This only validates single expressions.
254      * @param expression the expression to validate
255      * @param expressionPrefix the prefix used for this expression. if the expression is ${bean:msg.header}
256      * the prefix is "${"
257      * @return true if the expression evaluator is recognised
258      */
259     public static boolean isValidExpression(String expression, String expressionPrefix)
260     {
261         if(expression.startsWith(expressionPrefix))
262         {
263             expression = expression.substring(2, expression.length()-1);
264         }
265         String name;
266         int i = expression.indexOf(":");
267         if(i>-1)
268         {
269             name = expression.substring(0, i);
270         }
271         else
272         {
273             name = expression;
274         }
275         return isEvaluatorRegistered(name);
276     }
277 
278 }