KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > util > ExpressionEvaluationUtils


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.web.util;
18
19 import java.util.Collections JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Map JavaDoc;
22
23 import javax.servlet.ServletContext JavaDoc;
24 import javax.servlet.jsp.JspException JavaDoc;
25 import javax.servlet.jsp.PageContext JavaDoc;
26 import javax.servlet.jsp.el.ELException JavaDoc;
27 import javax.servlet.jsp.el.Expression JavaDoc;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
32
33 import org.springframework.util.Assert;
34 import org.springframework.util.ClassUtils;
35
36 /**
37  * Convenience methods for transparent access to JSP 2.0's built-in
38  * {@link javax.servlet.jsp.el.ExpressionEvaluator} or the standalone
39  * {@link org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager}
40  * of Jakarta's JSTL implementation.
41  *
42  * <p>Automatically detects JSP 2.0 or Jakarta JSTL, preferring the JSP 2.0
43  * mechanism if available. Also detects the JSP 2.0 API being present but
44  * the runtime JSP engine not actually supporting JSP 2.0, falling back to
45  * the Jakarta JSTL in this case. Throws an exception when encountering actual
46  * EL expressions if neither JSP 2.0 nor the Jakarta JSTL is available.
47  *
48  * <p>In the case of JSP 2.0, this class will by default use standard
49  * <code>evaluate</code> calls. If your application server happens to be
50  * inefficient in that respect, consider setting Spring's "cacheJspExpressions"
51  * context-param in <code>web.xml</code> to "true", which will use
52  * <code>parseExpression</code> calls with cached Expression objects instead.
53  *
54  * <p>The evaluation methods check if the value contains "${" before
55  * invoking the EL evaluator, treating the value as "normal" expression
56  * (i.e. a literal String value) else.
57  *
58  * <p>Note: The evaluation methods do not have a runtime dependency on
59  * JSP 2.0 or on Jakarta's JSTL implementation, as long as they are not
60  * asked to process actual EL expressions. This allows for using EL-aware
61  * tags with Java-based JSP expressions instead, for example.
62  *
63  * @author Juergen Hoeller
64  * @author Alef Arendsen
65  * @since 11.07.2003
66  * @see javax.servlet.jsp.el.ExpressionEvaluator#evaluate
67  * @see javax.servlet.jsp.el.ExpressionEvaluator#parseExpression
68  * @see org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager
69  */

70 public abstract class ExpressionEvaluationUtils {
71
72     /**
73      * JSP 2.0 expression cache parameter at the servlet context level
74      * (i.e. a context-param in <code>web.xml</code>): "cacheJspExpressions".
75      */

76     public static final String JavaDoc EXPRESSION_CACHE_CONTEXT_PARAM = "cacheJspExpressions";
77
78     public static final String JavaDoc EXPRESSION_PREFIX = "${";
79
80     public static final String JavaDoc EXPRESSION_SUFFIX = "}";
81
82
83     private static final String JavaDoc EXPRESSION_CACHE_FLAG_CONTEXT_ATTR =
84             ExpressionEvaluationUtils.class.getName() + ".CACHE_JSP_EXPRESSIONS";
85
86     private static final String JavaDoc EXPRESSION_CACHE_MAP_CONTEXT_ATTR =
87             ExpressionEvaluationUtils.class.getName() + ".JSP_EXPRESSION_CACHE";
88
89     private static final String JavaDoc JSP_20_CLASS_NAME =
90             "javax.servlet.jsp.el.ExpressionEvaluator";
91
92     private static final String JavaDoc JAKARTA_JSTL_CLASS_NAME =
93             "org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager";
94
95
96     private static final Log logger = LogFactory.getLog(ExpressionEvaluationUtils.class);
97
98     private static ExpressionEvaluationHelper helper;
99
100
101     static {
102         ClassLoader JavaDoc cl = ExpressionEvaluationUtils.class.getClassLoader();
103         if (ClassUtils.isPresent(JSP_20_CLASS_NAME, cl)) {
104             logger.debug("Found JSP 2.0 ExpressionEvaluator");
105             if (ClassUtils.isPresent(JAKARTA_JSTL_CLASS_NAME, cl)) {
106                 logger.debug("Found Jakarta JSTL ExpressionEvaluatorManager");
107                 helper = new Jsp20ExpressionEvaluationHelper(new JakartaExpressionEvaluationHelper());
108             }
109             else {
110                 helper = new Jsp20ExpressionEvaluationHelper(new NoExpressionEvaluationHelper());
111             }
112         }
113         else if (ClassUtils.isPresent(JAKARTA_JSTL_CLASS_NAME, cl)) {
114             logger.debug("Found Jakarta JSTL ExpressionEvaluatorManager");
115             helper = new JakartaExpressionEvaluationHelper();
116         }
117         else {
118             logger.debug("JSP expression evaluation not available");
119             helper = new NoExpressionEvaluationHelper();
120         }
121     }
122
123
124     /**
125      * Check if the given expression value is an EL expression.
126      * @param value the expression to check
127      * @return <code>true</code> if the expression is an EL expression,
128      * <code>false</code> otherwise
129      */

130     public static boolean isExpressionLanguage(String JavaDoc value) {
131         return (value != null && value.indexOf(EXPRESSION_PREFIX) != -1);
132     }
133
134     /**
135      * Evaluate the given expression (be it EL or a literal String value)
136      * to an Object of a given type,
137      * @param attrName name of the attribute (typically a JSP tag attribute)
138      * @param attrValue value of the attribute
139      * @param resultClass class that the result should have (String, Integer, Boolean)
140      * @param pageContext current JSP PageContext
141      * @return the result of the evaluation
142      * @throws JspException in case of parsing errors, also in case of type mismatch
143      * if the passed-in literal value is not an EL expression and not assignable to
144      * the result class
145      */

146     public static Object JavaDoc evaluate(String JavaDoc attrName, String JavaDoc attrValue, Class JavaDoc resultClass, PageContext JavaDoc pageContext)
147         throws JspException JavaDoc {
148
149         if (isExpressionLanguage(attrValue)) {
150             return doEvaluate(attrName, attrValue, resultClass, pageContext);
151         }
152         else if (attrValue != null && resultClass != null && !resultClass.isInstance(attrValue)) {
153             throw new JspException JavaDoc("Attribute value \"" + attrValue + "\" is neither a JSP EL expression nor " +
154                     "assignable to result class [" + resultClass.getName() + "]");
155         }
156         else {
157             return attrValue;
158         }
159     }
160
161     /**
162      * Evaluate the given expression (be it EL or a literal String value) to an Object.
163      * @param attrName name of the attribute (typically a JSP tag attribute)
164      * @param attrValue value of the attribute
165      * @param pageContext current JSP PageContext
166      * @return the result of the evaluation
167      * @throws JspException in case of parsing errors
168      */

169     public static Object JavaDoc evaluate(String JavaDoc attrName, String JavaDoc attrValue, PageContext JavaDoc pageContext)
170         throws JspException JavaDoc {
171
172         if (isExpressionLanguage(attrValue)) {
173             return doEvaluate(attrName, attrValue, Object JavaDoc.class, pageContext);
174         }
175         else {
176             return attrValue;
177         }
178     }
179
180     /**
181      * Evaluate the given expression (be it EL or a literal String value) to a String.
182      * @param attrName name of the attribute (typically a JSP tag attribute)
183      * @param attrValue value of the attribute
184      * @param pageContext current JSP PageContext
185      * @return the result of the evaluation
186      * @throws JspException in case of parsing errors
187      */

188     public static String JavaDoc evaluateString(String JavaDoc attrName, String JavaDoc attrValue, PageContext JavaDoc pageContext)
189         throws JspException JavaDoc {
190
191         if (isExpressionLanguage(attrValue)) {
192             return (String JavaDoc) doEvaluate(attrName, attrValue, String JavaDoc.class, pageContext);
193         }
194         else {
195             return attrValue;
196         }
197     }
198
199     /**
200      * Evaluate the given expression (be it EL or a literal String value) to an integer.
201      * @param attrName name of the attribute (typically a JSP tag attribute)
202      * @param attrValue value of the attribute
203      * @param pageContext current JSP PageContext
204      * @return the result of the evaluation
205      * @throws JspException in case of parsing errors
206      */

207     public static int evaluateInteger(String JavaDoc attrName, String JavaDoc attrValue, PageContext JavaDoc pageContext)
208             throws JspException JavaDoc {
209
210         if (isExpressionLanguage(attrValue)) {
211             return ((Integer JavaDoc) doEvaluate(attrName, attrValue, Integer JavaDoc.class, pageContext)).intValue();
212         }
213         else {
214             return Integer.parseInt(attrValue);
215         }
216     }
217
218     /**
219      * Evaluate the given expression (be it EL or a literal String value) to a boolean.
220      * @param attrName name of the attribute (typically a JSP tag attribute)
221      * @param attrValue value of the attribute
222      * @param pageContext current JSP PageContext
223      * @return the result of the evaluation
224      * @throws JspException in case of parsing errors
225      */

226     public static boolean evaluateBoolean(String JavaDoc attrName, String JavaDoc attrValue, PageContext JavaDoc pageContext)
227         throws JspException JavaDoc {
228
229         if (isExpressionLanguage(attrValue)) {
230             return ((Boolean JavaDoc) doEvaluate(attrName, attrValue, Boolean JavaDoc.class, pageContext)).booleanValue();
231         }
232         else {
233             return Boolean.valueOf(attrValue).booleanValue();
234         }
235     }
236
237
238     /**
239      * Actually evaluate the given expression (be it EL or a literal String value)
240      * to an Object of a given type. Supports concatenated expressions,
241      * for example: "${var1}text${var2}"
242      * @param attrName name of the attribute
243      * @param attrValue value of the attribute
244      * @param resultClass class that the result should have
245      * @param pageContext current JSP PageContext
246      * @return the result of the evaluation
247      * @throws JspException in case of parsing errors
248      */

249     private static Object JavaDoc doEvaluate(String JavaDoc attrName, String JavaDoc attrValue, Class JavaDoc resultClass, PageContext JavaDoc pageContext)
250         throws JspException JavaDoc {
251
252         Assert.notNull(attrValue, "Attribute value must not be null");
253         Assert.notNull(resultClass, "Result class must not be null");
254         Assert.notNull(pageContext, "PageContext must not be null");
255
256         if (resultClass.isAssignableFrom(String JavaDoc.class)) {
257             StringBuffer JavaDoc resultValue = null;
258             int exprPrefixIndex = -1;
259             int exprSuffixIndex = 0;
260             do {
261                 exprPrefixIndex = attrValue.indexOf(EXPRESSION_PREFIX, exprSuffixIndex);
262                 if (exprPrefixIndex != -1) {
263                     int prevExprSuffixIndex = exprSuffixIndex;
264                     exprSuffixIndex = attrValue.indexOf(EXPRESSION_SUFFIX, exprPrefixIndex + EXPRESSION_PREFIX.length());
265                     String JavaDoc expr = null;
266                     if (exprSuffixIndex != -1) {
267                         exprSuffixIndex += EXPRESSION_SUFFIX.length();
268                         expr = attrValue.substring(exprPrefixIndex, exprSuffixIndex);
269                     }
270                     else {
271                         expr = attrValue.substring(exprPrefixIndex);
272                     }
273                     if (expr.length() == attrValue.length()) {
274                         // A single expression without static prefix or suffix ->
275
// parse it with the specified result class rather than String.
276
return helper.evaluate(attrName, attrValue, resultClass, pageContext);
277                     }
278                     else {
279                         // We actually need to concatenate partial expressions into a String.
280
if (resultValue == null) {
281                             resultValue = new StringBuffer JavaDoc();
282                         }
283                         resultValue.append(attrValue.substring(prevExprSuffixIndex, exprPrefixIndex));
284                         resultValue.append(helper.evaluate(attrName, expr, String JavaDoc.class, pageContext));
285                     }
286                 }
287                 else {
288                     if (resultValue == null) {
289                         resultValue = new StringBuffer JavaDoc();
290                     }
291                     resultValue.append(attrValue.substring(exprSuffixIndex));
292                 }
293             }
294             while (exprPrefixIndex != -1 && exprSuffixIndex != -1);
295             return resultValue.toString();
296         }
297
298         else {
299             return helper.evaluate(attrName, attrValue, resultClass, pageContext);
300         }
301     }
302
303     /**
304      * Determine whether JSP 2.0 expressions are supposed to be cached
305      * and return the corresponding cache Map, or <code>null</code> if
306      * caching is not enabled.
307      * @param pageContext current JSP PageContext
308      * @return the cache Map, or <code>null</code> if caching is disabled
309      */

310     private static Map JavaDoc getJspExpressionCache(PageContext JavaDoc pageContext) {
311         ServletContext JavaDoc servletContext = pageContext.getServletContext();
312         Map JavaDoc cacheMap = (Map JavaDoc) servletContext.getAttribute(EXPRESSION_CACHE_MAP_CONTEXT_ATTR);
313         if (cacheMap == null) {
314             Boolean JavaDoc cacheFlag = (Boolean JavaDoc) servletContext.getAttribute(EXPRESSION_CACHE_FLAG_CONTEXT_ATTR);
315             if (cacheFlag == null) {
316                 cacheFlag = Boolean.valueOf(servletContext.getInitParameter(EXPRESSION_CACHE_CONTEXT_PARAM));
317                 servletContext.setAttribute(EXPRESSION_CACHE_FLAG_CONTEXT_ATTR, cacheFlag);
318             }
319             if (cacheFlag.booleanValue()) {
320                 cacheMap = Collections.synchronizedMap(new HashMap JavaDoc());
321                 servletContext.setAttribute(EXPRESSION_CACHE_MAP_CONTEXT_ATTR, cacheMap);
322             }
323         }
324         return cacheMap;
325     }
326
327
328     /**
329      * Internal interface for evaluating a JSP EL expression.
330      */

331     private static interface ExpressionEvaluationHelper {
332
333         public Object JavaDoc evaluate(String JavaDoc attrName, String JavaDoc attrValue, Class JavaDoc resultClass, PageContext JavaDoc pageContext)
334                 throws JspException JavaDoc;
335     }
336
337
338     /**
339      * Fallback ExpressionEvaluationHelper:
340      * always throws an exception in case of an actual EL expression.
341      */

342     private static class NoExpressionEvaluationHelper implements ExpressionEvaluationHelper {
343
344         public Object JavaDoc evaluate(String JavaDoc attrName, String JavaDoc attrValue, Class JavaDoc resultClass, PageContext JavaDoc pageContext)
345                 throws JspException JavaDoc {
346
347             throw new JspException JavaDoc(
348                     "Neither JSP 2.0 nor Jakarta JSTL available - cannot parse JSP EL expression \"" + attrValue + "\"");
349         }
350     }
351
352
353     /**
354      * Actual invocation of the Jakarta ExpressionEvaluatorManager.
355      * In separate inner class to avoid runtime dependency on Jakarta's
356      * JSTL implementation, for evaluation of non-EL expressions.
357      */

358     private static class JakartaExpressionEvaluationHelper implements ExpressionEvaluationHelper {
359
360         public Object JavaDoc evaluate(String JavaDoc attrName, String JavaDoc attrValue, Class JavaDoc resultClass, PageContext JavaDoc pageContext)
361             throws JspException JavaDoc {
362
363             return ExpressionEvaluatorManager.evaluate(attrName, attrValue, resultClass, pageContext);
364         }
365     }
366
367
368     /**
369      * Actual invocation of the JSP 2.0 ExpressionEvaluator.
370      * In separate inner class to avoid runtime dependency on JSP 2.0,
371      * for evaluation of non-EL expressions.
372      */

373     private static class Jsp20ExpressionEvaluationHelper implements ExpressionEvaluationHelper {
374
375         private final ExpressionEvaluationHelper fallback;
376
377         private boolean fallbackNecessary = false;
378
379         public Jsp20ExpressionEvaluationHelper(ExpressionEvaluationHelper fallback) {
380             this.fallback = fallback;
381         }
382
383         public Object JavaDoc evaluate(String JavaDoc attrName, String JavaDoc attrValue, Class JavaDoc resultClass, PageContext JavaDoc pageContext)
384                 throws JspException JavaDoc {
385
386             if (isFallbackNecessary()) {
387                 return this.fallback.evaluate(attrName, attrValue, resultClass, pageContext);
388             }
389
390             try {
391                 Map JavaDoc expressionCache = getJspExpressionCache(pageContext);
392                 if (expressionCache != null) {
393                     // We are supposed to explicitly create and cache JSP Expression objects.
394
ExpressionCacheKey cacheKey = new ExpressionCacheKey(attrValue, resultClass);
395                     Expression JavaDoc expr = (Expression JavaDoc) expressionCache.get(cacheKey);
396                     if (expr == null) {
397                         expr = pageContext.getExpressionEvaluator().parseExpression(attrValue, resultClass, null);
398                         expressionCache.put(cacheKey, expr);
399                     }
400                     return expr.evaluate(pageContext.getVariableResolver());
401                 }
402                 else {
403                     // We're simply calling the JSP 2.0 evaluate method straight away.
404
return pageContext.getExpressionEvaluator().evaluate(
405                             attrValue, resultClass, pageContext.getVariableResolver(), null);
406                 }
407             }
408             catch (ELException JavaDoc ex) {
409                 throw new JspException JavaDoc("Parsing of JSP EL expression \"" + attrValue + "\" failed", ex);
410             }
411             catch (LinkageError JavaDoc err) {
412                 logger.debug("JSP 2.0 ExpressionEvaluator API present but not implemented - using fallback", err);
413                 setFallbackNecessary();
414                 return this.fallback.evaluate(attrName, attrValue, resultClass, pageContext);
415             }
416         }
417
418         private synchronized boolean isFallbackNecessary() {
419             return this.fallbackNecessary;
420         }
421
422         private synchronized void setFallbackNecessary() {
423             this.fallbackNecessary = true;
424         }
425     }
426
427
428     /**
429      * Cache key class for JSP 2.0 Expression objects.
430      */

431     private static class ExpressionCacheKey {
432
433         private final String JavaDoc value;
434         private final Class JavaDoc resultClass;
435         private final int hashCode;
436
437         public ExpressionCacheKey(String JavaDoc value, Class JavaDoc resultClass) {
438             this.value = value;
439             this.resultClass = resultClass;
440             this.hashCode = this.value.hashCode() * 29 + this.resultClass.hashCode();
441         }
442
443         public boolean equals(Object JavaDoc obj) {
444             if (!(obj instanceof ExpressionCacheKey)) {
445                 return false;
446             }
447             ExpressionCacheKey other = (ExpressionCacheKey) obj;
448             return (this.value.equals(other.value) && this.resultClass.equals(other.resultClass));
449         }
450
451         public int hashCode() {
452             return this.hashCode;
453         }
454     }
455
456 }
457
Popular Tags