KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > el > ExpressionEvaluatorImpl


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 1999 The Apache Software Foundation. All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in
16  * the documentation and/or other materials provided with the
17  * distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if
20  * any, must include the following acknowlegement:
21  * "This product includes software developed by the
22  * Apache Software Foundation (http://www.apache.org/)."
23  * Alternately, this acknowlegement may appear in the software itself,
24  * if and wherever such third-party acknowlegements normally appear.
25  *
26  * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
27  * Foundation" must not be used to endorse or promote products derived
28  * from this software without prior written permission. For written
29  * permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache"
32  * nor may "Apache" appear in their names without prior written
33  * permission of the Apache Group.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation. For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  */

55
56 package org.apache.commons.el;
57
58 import java.io.Reader JavaDoc;
59 import java.io.StringReader JavaDoc;
60 import java.text.MessageFormat JavaDoc;
61 import java.util.Collections JavaDoc;
62 import java.util.HashMap JavaDoc;
63 import java.util.Map JavaDoc;
64 import javax.servlet.jsp.el.ExpressionEvaluator JavaDoc;
65 import javax.servlet.jsp.el.ELException JavaDoc;
66 import javax.servlet.jsp.el.VariableResolver JavaDoc;
67 import javax.servlet.jsp.el.FunctionMapper JavaDoc;
68 import org.apache.commons.el.parser.ELParser;
69 import org.apache.commons.el.parser.ParseException;
70 import org.apache.commons.el.parser.Token;
71 import org.apache.commons.el.parser.TokenMgrError;
72
73 /**
74  *
75  * <p>This is the main class for evaluating expression Strings. An
76  * expression String is a String that may contain expressions of the
77  * form ${...}. Multiple expressions may appear in the same
78  * expression String. In such a case, the expression String's value
79  * is computed by concatenating the String values of those evaluated
80  * expressions and any intervening non-expression text, then
81  * converting the resulting String to the expected type using the
82  * PropertyEditor mechanism.
83  *
84  * <p>In the special case where the expression String is a single
85  * expression, the value of the expression String is determined by
86  * evaluating the expression, without any intervening conversion to a
87  * String.
88  *
89  * <p>The evaluator maintains a cache mapping expression Strings to
90  * their parsed results. For expression Strings containing no
91  * expression elements, it maintains a cache mapping
92  * ExpectedType/ExpressionString to parsed value, so that static
93  * expression Strings won't have to go through a conversion step every
94  * time they are used. All instances of the evaluator share the same
95  * cache. The cache may be bypassed by setting a flag on the
96  * evaluator's constructor.
97  *
98  * <p>The evaluator must be passed a VariableResolver in its
99  * constructor. The VariableResolver is used to resolve variable
100  * names encountered in expressions, and can also be used to implement
101  * "implicit objects" that are always present in the namespace.
102  * Different applications will have different policies for variable
103  * lookups and implicit objects - these differences can be
104  * encapsulated in the VariableResolver passed to the evaluator's
105  * constructor.
106  *
107  * <p>Most VariableResolvers will need to perform their resolution
108  * against some context. For example, a JSP environment needs a
109  * PageContext to resolve variables. The evaluate() method takes a
110  * generic Object context which is eventually passed to the
111  * VariableResolver - the VariableResolver is responsible for casting
112  * the context to the proper type.
113  *
114  * <p>Once an evaluator instance has been constructed, it may be used
115  * multiple times, and may be used by multiple simultaneous Threads.
116  * In other words, an evaluator instance is well-suited for use as a
117  * singleton.
118  *
119  * @author Nathan Abramson - Art Technology Group
120  * @author Shawn Bayern
121  * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: luehe $
122  **/

123
124 public class ExpressionEvaluatorImpl
125   extends ExpressionEvaluator JavaDoc
126 {
127   //-------------------------------------
128
// Properties
129
//-------------------------------------
130

131   //-------------------------------------
132
// Member variables
133
//-------------------------------------
134

135   /** The mapping from expression String to its parsed form (String,
136       Expression, or ExpressionString) **/

137   static Map JavaDoc sCachedExpressionStrings =
138     Collections.synchronizedMap (new HashMap JavaDoc ());
139
140   /** The mapping from ExpectedType to Maps mapping literal String to
141       parsed value **/

142   static Map JavaDoc sCachedExpectedTypes = new HashMap JavaDoc ();
143
144   /** The static Logger **/
145   static Logger sLogger = new Logger (System.out);
146
147   /** Flag if the cache should be bypassed **/
148   boolean mBypassCache;
149
150   //-------------------------------------
151
/**
152    *
153    * Constructor
154    **/

155   public ExpressionEvaluatorImpl () { }
156
157   /**
158    *
159    * Constructor
160    *
161    * @param pBypassCache flag indicating if the cache should be
162    * bypassed
163    **/

164   public ExpressionEvaluatorImpl (boolean pBypassCache)
165   {
166     mBypassCache = pBypassCache;
167   }
168
169   //-------------------------------------
170
/**
171    *
172    * Evaluates the given expression String
173    *
174    * @param pExpressionString The expression to be evaluated.
175    * @param pExpectedType The expected type of the result of the evaluation
176    * @param pResolver A VariableResolver instance that can be used at
177    * runtime to resolve the name of implicit objects into Objects.
178    * @param functions A FunctionMapper to resolve functions found in
179    * the expression. It can be null, in which case no functions
180    * are supported for this invocation.
181    * @return the expression String evaluated to the given expected
182    * type
183    **/

184   public Object JavaDoc evaluate (String JavaDoc pExpressionString,
185               Class JavaDoc pExpectedType,
186                           VariableResolver JavaDoc pResolver,
187               FunctionMapper JavaDoc functions)
188     throws ELException JavaDoc
189   {
190     return evaluate (pExpressionString,
191              pExpectedType,
192                      pResolver,
193              functions,
194              sLogger);
195   }
196
197   /**
198    *
199    * Prepare an expression for later evaluation. This method should perform
200    * syntactic validation of the expression; if in doing so it detects
201    * errors, it should raise an ELParseException.
202    *
203    * @param expression The expression to be evaluated.
204    * @param expectedType The expected type of the result of the evaluation
205    * @param fMapper A FunctionMapper to resolve functions found in
206    * the expression. It can be null, in which case no functions
207    * are supported for this invocation. The ExpressionEvaluator
208    * must not hold on to the FunctionMapper reference after
209    * returning from <code>parseExpression()</code>. The
210    * <code>Expression</code> object returned must invoke the same
211    * functions regardless of whether the mappings in the
212    * provided <code>FunctionMapper</code> instance change between
213    * calling <code>ExpressionEvaluator.parseExpression()</code>
214    * and <code>Expression.evaluate()</code>.
215    * @return The Expression object encapsulating the arguments.
216    *
217    * @exception ELException Thrown if parsing errors were found.
218    **/

219   public javax.servlet.jsp.el.Expression JavaDoc parseExpression(String JavaDoc expression,
220                              Class JavaDoc expectedType,
221                              FunctionMapper JavaDoc fMapper)
222     throws ELException JavaDoc
223   {
224     // Validate and then create an Expression object.
225
parseExpressionString(expression);
226         
227     // Create an Expression object that knows how to evaluate this.
228
return new JSTLExpression(this, expression, expectedType, fMapper);
229   }
230
231   //-------------------------------------
232
/**
233    *
234    * Evaluates the given expression string
235    **/

236   Object JavaDoc evaluate (String JavaDoc pExpressionString,
237            Class JavaDoc pExpectedType,
238            VariableResolver JavaDoc pResolver,
239            FunctionMapper JavaDoc functions,
240            Logger pLogger)
241     throws ELException JavaDoc
242   {
243     // Check for null expression strings
244
if (pExpressionString == null) {
245       throw new ELException JavaDoc
246     (Constants.NULL_EXPRESSION_STRING);
247     }
248
249     // Get the parsed version of the expression string
250
Object JavaDoc parsedValue = parseExpressionString (pExpressionString);
251
252     // Evaluate differently based on the parsed type
253
if (parsedValue instanceof String JavaDoc) {
254       // Convert the String, and cache the conversion
255
String JavaDoc strValue = (String JavaDoc) parsedValue;
256       return convertStaticValueToExpectedType (strValue,
257                            pExpectedType,
258                            pLogger);
259     }
260
261     else if (parsedValue instanceof Expression) {
262       // Evaluate the expression and convert
263
Object JavaDoc value =
264     ((Expression) parsedValue).evaluate (pResolver,
265                          functions,
266                          pLogger);
267       return convertToExpectedType (value,
268                     pExpectedType,
269                     pLogger);
270     }
271
272     else if (parsedValue instanceof ExpressionString) {
273       // Evaluate the expression/string list and convert
274
String JavaDoc strValue =
275     ((ExpressionString) parsedValue).evaluate (pResolver,
276                            functions,
277                            pLogger);
278       return convertToExpectedType (strValue,
279                     pExpectedType,
280                     pLogger);
281     }
282
283     else {
284       // This should never be reached
285
return null;
286     }
287   }
288
289   //-------------------------------------
290
/**
291    *
292    * Gets the parsed form of the given expression string. If the
293    * parsed form is cached (and caching is not bypassed), return the
294    * cached form, otherwise parse and cache the value. Returns either
295    * a String, Expression, or ExpressionString.
296    **/

297   public Object JavaDoc parseExpressionString (String JavaDoc pExpressionString)
298     throws ELException JavaDoc
299   {
300     // See if it's an empty String
301
if (pExpressionString.length () == 0) {
302       return "";
303     }
304
305     // See if it's in the cache
306
Object JavaDoc ret =
307       mBypassCache ?
308       null :
309       sCachedExpressionStrings.get (pExpressionString);
310
311     if (ret == null) {
312       // Parse the expression
313
Reader JavaDoc r = new StringReader JavaDoc (pExpressionString);
314       ELParser parser = new ELParser (r);
315       try {
316     ret = parser.ExpressionString ();
317     sCachedExpressionStrings.put (pExpressionString, ret);
318       }
319       catch (ParseException exc) {
320     throw new ELException JavaDoc
321       (formatParseException (pExpressionString,
322                  exc));
323       }
324       catch (TokenMgrError exc) {
325     // Note - this should never be reached, since the parser is
326
// constructed to tokenize any input (illegal inputs get
327
// parsed to <BADLY_ESCAPED_STRING_LITERAL> or
328
// <ILLEGAL_CHARACTER>
329
throw new ELException JavaDoc (exc.getMessage ());
330       }
331     }
332     return ret;
333   }
334
335   //-------------------------------------
336
/**
337    *
338    * Converts the given value to the specified expected type.
339    **/

340   Object JavaDoc convertToExpectedType (Object JavaDoc pValue,
341                 Class JavaDoc pExpectedType,
342                 Logger pLogger)
343     throws ELException JavaDoc
344   {
345     return Coercions.coerce (pValue,
346                  pExpectedType,
347                  pLogger);
348   }
349
350   //-------------------------------------
351
/**
352    *
353    * Converts the given String, specified as a static expression
354    * string, to the given expected type. The conversion is cached.
355    **/

356   Object JavaDoc convertStaticValueToExpectedType (String JavaDoc pValue,
357                        Class JavaDoc pExpectedType,
358                        Logger pLogger)
359     throws ELException JavaDoc
360   {
361     // See if the value is already of the expected type
362
if (pExpectedType == String JavaDoc.class ||
363     pExpectedType == Object JavaDoc.class) {
364       return pValue;
365     }
366
367     // Find the cached value
368
Map JavaDoc valueByString = getOrCreateExpectedTypeMap (pExpectedType);
369     if (!mBypassCache &&
370     valueByString.containsKey (pValue)) {
371       return valueByString.get (pValue);
372     }
373     else {
374       // Convert from a String
375
Object JavaDoc ret = Coercions.coerce (pValue, pExpectedType, pLogger);
376       valueByString.put (pValue, ret);
377       return ret;
378     }
379   }
380
381   //-------------------------------------
382
/**
383    *
384    * Creates or returns the Map that maps string literals to parsed
385    * values for the specified expected type.
386    **/

387   static Map JavaDoc getOrCreateExpectedTypeMap (Class JavaDoc pExpectedType)
388   {
389     synchronized (sCachedExpectedTypes) {
390       Map JavaDoc ret = (Map JavaDoc) sCachedExpectedTypes.get (pExpectedType);
391       if (ret == null) {
392     ret = Collections.synchronizedMap (new HashMap JavaDoc ());
393     sCachedExpectedTypes.put (pExpectedType, ret);
394       }
395       return ret;
396     }
397   }
398
399   //-------------------------------------
400
// Formatting ParseException
401
//-------------------------------------
402
/**
403    *
404    * Formats a ParseException into an error message suitable for
405    * displaying on a web page
406    **/

407   static String JavaDoc formatParseException (String JavaDoc pExpressionString,
408                       ParseException pExc)
409   {
410     // Generate the String of expected tokens
411
StringBuffer JavaDoc expectedBuf = new StringBuffer JavaDoc ();
412     int maxSize = 0;
413     boolean printedOne = false;
414
415     if (pExc.expectedTokenSequences == null)
416       return pExc.toString();
417
418     for (int i = 0; i < pExc.expectedTokenSequences.length; i++) {
419       if (maxSize < pExc.expectedTokenSequences [i].length) {
420         maxSize = pExc.expectedTokenSequences [i].length;
421       }
422       for (int j = 0; j < pExc.expectedTokenSequences [i].length; j++) {
423     if (printedOne) {
424       expectedBuf.append (", ");
425     }
426         expectedBuf.append
427       (pExc.tokenImage [pExc.expectedTokenSequences [i] [j]]);
428     printedOne = true;
429       }
430     }
431     String JavaDoc expected = expectedBuf.toString ();
432
433     // Generate the String of encountered tokens
434
StringBuffer JavaDoc encounteredBuf = new StringBuffer JavaDoc ();
435     Token tok = pExc.currentToken.next;
436     for (int i = 0; i < maxSize; i++) {
437       if (i != 0) encounteredBuf.append (" ");
438       if (tok.kind == 0) {
439         encounteredBuf.append (pExc.tokenImage [0]);
440         break;
441       }
442       encounteredBuf.append (addEscapes (tok.image));
443       tok = tok.next;
444     }
445     String JavaDoc encountered = encounteredBuf.toString ();
446
447     // Format the error message
448
return MessageFormat.format
449       (Constants.PARSE_EXCEPTION,
450        new Object JavaDoc [] {
451      expected,
452      encountered,
453        });
454   }
455
456   //-------------------------------------
457
/**
458    *
459    * Used to convert raw characters to their escaped version when
460    * these raw version cannot be used as part of an ASCII string
461    * literal.
462    **/

463   static String JavaDoc addEscapes (String JavaDoc str)
464   {
465     StringBuffer JavaDoc retval = new StringBuffer JavaDoc ();
466     char ch;
467     for (int i = 0; i < str.length (); i++) {
468       switch (str.charAt (i)) {
469     case 0 :
470       continue;
471     case '\b':
472       retval.append ("\\b");
473       continue;
474     case '\t':
475       retval.append ("\\t");
476       continue;
477     case '\n':
478       retval.append ("\\n");
479       continue;
480     case '\f':
481       retval.append ("\\f");
482       continue;
483     case '\r':
484       retval.append ("\\r");
485       continue;
486     default:
487       if ((ch = str.charAt (i)) < 0x20 || ch > 0x7e) {
488         String JavaDoc s = "0000" + Integer.toString (ch, 16);
489         retval.append ("\\u" + s.substring (s.length () - 4, s.length ()));
490       }
491       else {
492         retval.append (ch);
493       }
494       continue;
495         }
496     }
497     return retval.toString ();
498   }
499
500   //-------------------------------------
501
// Testing methods
502
//-------------------------------------
503
/**
504    *
505    * Parses the given expression string, then converts it back to a
506    * String in its canonical form. This is used to test parsing.
507    **/

508   public String JavaDoc parseAndRender (String JavaDoc pExpressionString)
509     throws ELException JavaDoc
510   {
511     Object JavaDoc val = parseExpressionString (pExpressionString);
512     if (val instanceof String JavaDoc) {
513       return (String JavaDoc) val;
514     }
515     else if (val instanceof Expression) {
516       return "${" + ((Expression) val).getExpressionString () + "}";
517     }
518     else if (val instanceof ExpressionString) {
519       return ((ExpressionString) val).getExpressionString ();
520     }
521     else {
522       return "";
523     }
524   }
525
526   /**
527    * An object that encapsulates an expression to be evaluated by
528    * the JSTL evaluator.
529    */

530   private class JSTLExpression
531     extends javax.servlet.jsp.el.Expression JavaDoc
532   {
533     private ExpressionEvaluatorImpl evaluator;
534     private String JavaDoc expression;
535     private Class JavaDoc expectedType;
536     private FunctionMapper JavaDoc fMapper;
537
538     public JSTLExpression(ExpressionEvaluatorImpl evaluator, String JavaDoc expression,
539               Class JavaDoc expectedType, FunctionMapper JavaDoc fMapper)
540     {
541       this.evaluator = evaluator;
542       this.expression = expression;
543       this.expectedType = expectedType;
544       this.fMapper = fMapper;
545     }
546         
547     public Object JavaDoc evaluate( VariableResolver JavaDoc vResolver )
548       throws ELException JavaDoc
549     {
550       return evaluator.evaluate(this.expression,
551                 this.expectedType,
552                 vResolver,
553                 this.fMapper);
554     }
555   }
556
557   //-------------------------------------
558

559 }
560
Popular Tags