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.el;
018    
019    import java.io.Reader;
020    import java.io.StringReader;
021    import java.text.MessageFormat;
022    import java.util.Collections;
023    import java.util.HashMap;
024    import java.util.Map;
025    
026    import javax.servlet.jsp.el.ELException;
027    import javax.servlet.jsp.el.ExpressionEvaluator;
028    import javax.servlet.jsp.el.FunctionMapper;
029    import javax.servlet.jsp.el.VariableResolver;
030    
031    import org.apache.commons.el.parser.ELParser;
032    import org.apache.commons.el.parser.ParseException;
033    import org.apache.commons.el.parser.Token;
034    import org.apache.commons.el.parser.TokenMgrError;
035    
036    /**
037     *
038     * <p>This is the main class for evaluating expression Strings.  An
039     * expression String is a String that may contain expressions of the
040     * form ${...}.  Multiple expressions may appear in the same
041     * expression String.  In such a case, the expression String's value
042     * is computed by concatenating the String values of those evaluated
043     * expressions and any intervening non-expression text, then
044     * converting the resulting String to the expected type using the
045     * PropertyEditor mechanism.
046     *
047     * <p>In the special case where the expression String is a single
048     * expression, the value of the expression String is determined by
049     * evaluating the expression, without any intervening conversion to a
050     * String.
051     *
052     * <p>The evaluator maintains a cache mapping expression Strings to
053     * their parsed results.  For expression Strings containing no
054     * expression elements, it maintains a cache mapping
055     * ExpectedType/ExpressionString to parsed value, so that static
056     * expression Strings won't have to go through a conversion step every
057     * time they are used.  All instances of the evaluator share the same
058     * cache.  The cache may be bypassed by setting a flag on the
059     * evaluator's constructor.
060     *
061     * <p>The evaluator must be passed a VariableResolver in its
062     * constructor.  The VariableResolver is used to resolve variable
063     * names encountered in expressions, and can also be used to implement
064     * "implicit objects" that are always present in the namespace.
065     * Different applications will have different policies for variable
066     * lookups and implicit objects - these differences can be
067     * encapsulated in the VariableResolver passed to the evaluator's
068     * constructor.
069     *
070     * <p>Most VariableResolvers will need to perform their resolution
071     * against some context.  For example, a JSP environment needs a
072     * PageContext to resolve variables.  The evaluate() method takes a
073     * generic Object context which is eventually passed to the
074     * VariableResolver - the VariableResolver is responsible for casting
075     * the context to the proper type.
076     *
077     * <p>Once an evaluator instance has been constructed, it may be used
078     * multiple times, and may be used by multiple simultaneous Threads.
079     * In other words, an evaluator instance is well-suited for use as a
080     * singleton.
081     * 
082     * @author Nathan Abramson - Art Technology Group
083     * @author Shawn Bayern
084     * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: bayard $
085     **/
086    public class ExpressionEvaluatorImpl
087      extends ExpressionEvaluator
088    {
089      //-------------------------------------
090      // Statics
091      //-------------------------------------
092      /** The mapping from expression String to its parsed form (String,
093          Expression, or ExpressionString) **/
094      static Map sCachedExpressionStrings =
095        Collections.synchronizedMap (new HashMap ());
096    
097      /** The mapping from ExpectedType to Maps mapping literal String to
098          parsed value **/
099      static Map sCachedExpectedTypes = new HashMap ();
100    
101      //-------------------------------------
102      // Member variables
103      //-------------------------------------
104    
105      /** Flag if the cache should be bypassed **/
106      boolean mBypassCache;
107    
108      //-------------------------------------
109      /**
110       *
111       * Constructor
112       **/
113      public ExpressionEvaluatorImpl () { }
114    
115      /**
116       *
117       * Constructor
118       *
119       * @param pBypassCache flag indicating if the cache should be
120       * bypassed
121       **/
122      public ExpressionEvaluatorImpl (boolean pBypassCache)
123      {
124        mBypassCache = pBypassCache;
125      }
126    
127      //-------------------------------------
128    
129      /**
130       *
131       * Prepare an expression for later evaluation.  This method should perform
132       * syntactic validation of the expression; if in doing so it detects 
133       * errors, it should raise an ELParseException.
134       *
135       * @param expression The expression to be evaluated.
136       * @param expectedType The expected type of the result of the evaluation
137       * @param fMapper A FunctionMapper to resolve functions found in
138       *     the expression.  It can be null, in which case no functions
139       *     are supported for this invocation.  The ExpressionEvaluator
140       *     must not hold on to the FunctionMapper reference after
141       *     returning from <code>parseExpression()</code>.  The
142       *     <code>Expression</code> object returned must invoke the same
143       *     functions regardless of whether the mappings in the
144       *     provided <code>FunctionMapper</code> instance change between
145       *     calling <code>ExpressionEvaluator.parseExpression()</code>
146       *     and <code>Expression.evaluate()</code>.
147       * @return The Expression object encapsulating the arguments.
148       *
149       * @exception ELException Thrown if parsing errors were found.
150       **/
151      public javax.servlet.jsp.el.Expression parseExpression(String expression,
152                                                            Class expectedType,
153                                                            FunctionMapper fMapper)
154        throws ELException
155      {
156           // Create an Expression object that knows how to evaluate this.
157           final Object parsedExpression = parseExpressionString(expression);
158           if (parsedExpression instanceof Expression) {
159               return new JSTLExpression(this, (Expression)parsedExpression, expectedType, fMapper);
160           } else {
161               // this had better be a string
162               return new JSTLExpression(this, (String)parsedExpression, expectedType, fMapper);
163           }
164      }
165    
166      //-------------------------------------
167      /**
168       *
169       * Evaluates the given expression String
170       *
171       * @param pExpressionString The expression to be evaluated.
172       * @param pExpectedType The expected type of the result of the evaluation
173       * @param pResolver A VariableResolver instance that can be used at 
174       *     runtime to resolve the name of implicit objects into Objects.
175       * @param functions A FunctionMapper to resolve functions found in 
176       *     the expression.  It can be null, in which case no functions 
177       *     are supported for this invocation.
178       * @return the expression String evaluated to the given expected type
179       **/
180      public Object evaluate (String pExpressionString,
181                    Class pExpectedType,
182                    VariableResolver pResolver,
183                    FunctionMapper functions)
184        throws ELException
185      {
186        // Check for null expression strings
187        if (pExpressionString == null) {
188          throw new ELException
189           (Constants.NULL_EXPRESSION_STRING);
190        }
191    
192        // Get the parsed version of the expression string
193        Object parsedValue = parseExpressionString (pExpressionString);
194        return evaluate (parsedValue, pExpectedType, pResolver, functions);
195      }
196    
197      //-------------------------------------
198      /**
199       *
200       * Evaluates the given parsed expression.
201       *
202       * @param parsedExpression The expression to be evaluated.
203       * @param pExpectedType The expected type of the result of the evaluation
204       * @param pResolver A VariableResolver instance that can be used at 
205       *     runtime to resolve the name of implicit objects into Objects.
206       * @param functions A FunctionMapper to resolve functions found in 
207       *     the expression.  It can be null, in which case no functions 
208       *     are supported for this invocation.
209       * @return the expression evaluated to the given expected type
210       **/
211      public Object evaluate (Object parsedExpression, Class pExpectedType,
212          VariableResolver pResolver, FunctionMapper functions) throws ELException
213      {
214          return evaluateParsedValue(parsedExpression, pExpectedType, pResolver, functions);
215      }
216    
217      private Object evaluateParsedValue(Object parsedValue, Class pExpectedType, VariableResolver pResolver, FunctionMapper functions) throws ELException {
218            // Evaluate differently based on the parsed type
219            if (parsedValue instanceof String) {
220              // Convert the String, and cache the conversion
221              String strValue = (String) parsedValue;
222              return convertStaticValueToExpectedType (strValue, pExpectedType);
223            }
224    
225            else if (parsedValue instanceof Expression) {
226              // Evaluate the expression and convert
227              Object value =
228            ((Expression) parsedValue).evaluate (pResolver,
229                                functions);
230              return convertToExpectedType (value, pExpectedType);
231            }
232    
233            else {
234              // This should never be reached
235              return null;
236            }
237        }
238    
239      //-------------------------------------
240      /**
241       *
242       * Gets the parsed form of the given expression string.  If the
243       * parsed form is cached (and caching is not bypassed), return the
244       * cached form, otherwise parse and cache the value.  Returns either
245       * a String, Expression, or ExpressionString.
246       **/
247      public Object parseExpressionString (String pExpressionString)
248        throws ELException
249      {
250        // See if it's an empty String
251        if (pExpressionString.length () == 0) {
252          return "";
253        }
254    
255        // See if it's in the cache
256        Object ret =
257          mBypassCache ?
258          null :
259          sCachedExpressionStrings.get (pExpressionString);
260    
261        if (ret == null) {
262          // Parse the expression
263          Reader r = new StringReader (pExpressionString);
264          ELParser parser = new ELParser (r);
265          try {
266            ret = parser.ExpressionString ();
267            sCachedExpressionStrings.put (pExpressionString, ret);
268          }
269          catch (ParseException exc)
270          {
271            throw new ELException
272              (formatParseException (pExpressionString,
273                  exc));
274          }
275          catch (TokenMgrError exc)
276          {
277            // Note - this should never be reached, since the parser is
278            // constructed to tokenize any input (illegal inputs get
279            // parsed to <BADLY_ESCAPED_STRING_LITERAL> or
280            // <ILLEGAL_CHARACTER>
281            throw new ELException (exc.getMessage ());
282          }
283        }
284        return ret;
285      }
286    
287      //-------------------------------------
288      /**
289       *
290       * Converts the given value to the specified expected type.
291       **/
292      Object convertToExpectedType (Object pValue,
293                                   Class pExpectedType)
294        throws ELException
295      {
296        return Coercions.coerce (pValue, pExpectedType);
297      }
298    
299      //-------------------------------------
300      /**
301       *
302       * Converts the given String, specified as a static expression
303       * string, to the given expected type.  The conversion is cached.
304       **/
305      Object convertStaticValueToExpectedType (String pValue, Class pExpectedType)
306        throws ELException
307      {
308        // See if the value is already of the expected type
309        if (pExpectedType == String.class ||
310          pExpectedType == Object.class) {
311          return pValue;
312        }
313    
314        // Find the cached value
315        Map valueByString = getOrCreateExpectedTypeMap (pExpectedType);
316        if (!mBypassCache &&
317          valueByString.containsKey (pValue)) {
318          return valueByString.get (pValue);
319        }
320        else {
321          // Convert from a String
322          Object ret = Coercions.coerce (pValue, pExpectedType);
323          valueByString.put (pValue, ret);
324          return ret;
325        }
326      }
327    
328      //-------------------------------------
329      /**
330       *
331       * Creates or returns the Map that maps string literals to parsed
332       * values for the specified expected type.
333       **/
334      static Map getOrCreateExpectedTypeMap (Class pExpectedType)
335      {
336        synchronized (sCachedExpectedTypes) {
337          Map ret = (Map) sCachedExpectedTypes.get (pExpectedType);
338          if (ret == null) {
339            ret = Collections.synchronizedMap (new HashMap ());
340            sCachedExpectedTypes.put (pExpectedType, ret);
341          }
342          return ret;
343        }
344      }
345    
346      //-------------------------------------
347      // Formatting ParseException
348      //-------------------------------------
349      /**
350       *
351       * Formats a ParseException into an error message suitable for
352       * displaying on a web page
353       **/
354      static String formatParseException (String pExpressionString,
355                                         ParseException pExc)
356      {
357        // Generate the String of expected tokens
358        StringBuffer expectedBuf = new StringBuffer ();
359        int maxSize = 0;
360        boolean printedOne = false;
361    
362        if (pExc.expectedTokenSequences == null)
363          return pExc.toString();
364    
365        for (int i = 0; i < pExc.expectedTokenSequences.length; i++) {
366          if (maxSize < pExc.expectedTokenSequences [i].length) {
367            maxSize = pExc.expectedTokenSequences [i].length;
368          }
369          for (int j = 0; j < pExc.expectedTokenSequences[i].length; j++) {
370            if (printedOne) {
371              expectedBuf.append (", ");
372            }
373            expectedBuf.append
374              (pExc.tokenImage [pExc.expectedTokenSequences [i] [j]]);
375            printedOne = true;
376          }
377        }
378        String expected = expectedBuf.toString ();
379    
380        // Generate the String of encountered tokens
381        StringBuffer encounteredBuf = new StringBuffer ();
382        Token tok = pExc.currentToken.next;
383        for (int i = 0; i < maxSize; i++) {
384          if (i != 0) encounteredBuf.append (" ");
385    
386          if (tok.kind == 0) {
387            encounteredBuf.append (pExc.tokenImage[0]);
388            break;
389          }
390          encounteredBuf.append (addEscapes (tok.image));
391          tok = tok.next;
392        }
393        String encountered = encounteredBuf.toString ();
394    
395        // Format the error message
396        return MessageFormat.format
397          (Constants.PARSE_EXCEPTION,
398           new Object[] {
399            expected,
400            encountered,
401           });
402      }
403    
404      //-------------------------------------
405      /**
406       *
407       * Used to convert raw characters to their escaped version when
408       * these raw version cannot be used as part of an ASCII string
409       * literal.
410       **/
411      static String addEscapes (String str)
412      {
413        StringBuffer retval = new StringBuffer ();
414        char ch;
415        for (int i = 0, length = str.length (); i < length; i++) {
416          switch (str.charAt (i)) {
417          case 0:
418            continue;
419          case '\b':
420            retval.append ("\\b");
421            continue;
422          case '\t':
423            retval.append ("\\t");
424            continue;
425          case '\n':
426            retval.append ("\\n");
427            continue;
428          case '\f':
429            retval.append ("\\f");
430            continue;
431          case '\r':
432            retval.append ("\\r");
433            continue;
434          default:
435            if ((ch = str.charAt (i)) < 0x20 || ch > 0x7e) {
436              String s = "0000" + Integer.toString (ch, 16);
437              retval.append ("\\u" + s.substring (s.length () - 4, s.length ()));
438            }
439            else {
440              retval.append (ch);
441            }
442            continue;
443          }
444        }
445        return retval.toString ();
446      }
447    
448      //-------------------------------------
449      // Testing methods
450      //-------------------------------------
451      /**
452       *
453       * Parses the given expression string, then converts it back to a
454       * String in its canonical form.  This is used to test parsing.
455       **/
456      public String parseAndRender (String pExpressionString)
457        throws ELException
458      {
459        Object val = parseExpressionString (pExpressionString);
460        if (val instanceof String) {
461          return (String) val;
462        }
463        else if (val instanceof Expression) {
464          return "${" + ((Expression) val).getExpressionString () + "}";
465        }
466        else if (val instanceof ExpressionString) {
467          return ((ExpressionString) val).getExpressionString ();
468        }
469        else {
470          return "";
471        }
472      }
473    
474      /**
475       * An object that encapsulates an expression to be evaluated by 
476       * the JSTL evaluator.
477       */
478      private class JSTLExpression
479        extends javax.servlet.jsp.el.Expression
480      {
481        private ExpressionEvaluatorImpl evaluator;
482        private Object parsedExpression;
483        private Class expectedType;
484    
485        public JSTLExpression(
486                final ExpressionEvaluatorImpl evaluator,
487                final Expression expression,
488                final Class expectedType,
489                final FunctionMapper fMapper)
490        throws ELException {
491          this.evaluator = evaluator;
492          this.parsedExpression = expression.bindFunctions(fMapper);
493          this.expectedType = expectedType;
494        }
495        public JSTLExpression(
496                final ExpressionEvaluatorImpl evaluator,
497                final String expressionString,
498                final Class expectedType,
499                final FunctionMapper fMapper)
500        throws ELException {
501           this.evaluator = evaluator;
502           this.parsedExpression = expressionString;
503           this.expectedType = expectedType;
504         }
505        
506         public Object evaluate( VariableResolver vResolver )
507           throws ELException
508         {
509          return evaluator.evaluateParsedValue(this.parsedExpression,
510                   this.expectedType,
511                   vResolver,
512                   null);
513         }
514       }
515    
516      //-------------------------------------
517    
518    }