KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > taglibs > standard > lang > jstl > ELEvaluator


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
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.apache.taglibs.standard.lang.jstl;
18
19 import java.io.Reader JavaDoc;
20 import java.io.StringReader JavaDoc;
21 import java.text.MessageFormat JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Map JavaDoc;
25
26 import org.apache.taglibs.standard.lang.jstl.parser.ELParser;
27 import org.apache.taglibs.standard.lang.jstl.parser.ParseException;
28 import org.apache.taglibs.standard.lang.jstl.parser.Token;
29 import org.apache.taglibs.standard.lang.jstl.parser.TokenMgrError;
30
31 /**
32  *
33  * <p>This is the main class for evaluating expression Strings. An
34  * expression String is a String that may contain expressions of the
35  * form ${...}. Multiple expressions may appear in the same
36  * expression String. In such a case, the expression String's value
37  * is computed by concatenating the String values of those evaluated
38  * expressions and any intervening non-expression text, then
39  * converting the resulting String to the expected type using the
40  * PropertyEditor mechanism.
41  *
42  * <p>In the special case where the expression String is a single
43  * expression, the value of the expression String is determined by
44  * evaluating the expression, without any intervening conversion to a
45  * String.
46  *
47  * <p>The evaluator maintains a cache mapping expression Strings to
48  * their parsed results. For expression Strings containing no
49  * expression elements, it maintains a cache mapping
50  * ExpectedType/ExpressionString to parsed value, so that static
51  * expression Strings won't have to go through a conversion step every
52  * time they are used. All instances of the evaluator share the same
53  * cache. The cache may be bypassed by setting a flag on the
54  * evaluator's constructor.
55  *
56  * <p>The evaluator must be passed a VariableResolver in its
57  * constructor. The VariableResolver is used to resolve variable
58  * names encountered in expressions, and can also be used to implement
59  * "implicit objects" that are always present in the namespace.
60  * Different applications will have different policies for variable
61  * lookups and implicit objects - these differences can be
62  * encapsulated in the VariableResolver passed to the evaluator's
63  * constructor.
64  *
65  * <p>Most VariableResolvers will need to perform their resolution
66  * against some context. For example, a JSP environment needs a
67  * PageContext to resolve variables. The evaluate() method takes a
68  * generic Object context which is eventually passed to the
69  * VariableResolver - the VariableResolver is responsible for casting
70  * the context to the proper type.
71  *
72  * <p>Once an evaluator instance has been constructed, it may be used
73  * multiple times, and may be used by multiple simultaneous Threads.
74  * In other words, an evaluator instance is well-suited for use as a
75  * singleton.
76  *
77  * @author Nathan Abramson - Art Technology Group
78  * @author Shawn Bayern
79  * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: pierred $
80  **/

81
82 public class ELEvaluator
83 {
84   //-------------------------------------
85
// Properties
86
//-------------------------------------
87

88   //-------------------------------------
89
// Member variables
90
//-------------------------------------
91

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

94   static Map JavaDoc sCachedExpressionStrings =
95     Collections.synchronizedMap (new HashMap JavaDoc ());
96
97   /** The mapping from ExpectedType to Maps mapping literal String to
98       parsed value **/

99   static Map JavaDoc sCachedExpectedTypes = new HashMap JavaDoc ();
100
101   /** The static Logger **/
102   static Logger sLogger = new Logger (System.out);
103
104   /** The VariableResolver **/
105   VariableResolver mResolver;
106
107   /** Flag if the cache should be bypassed **/
108   boolean mBypassCache;
109
110   //-------------------------------------
111
/**
112    *
113    * Constructor
114    *
115    * @param pResolver the object that should be used to resolve
116    * variable names encountered in expressions. If null, all variable
117    * references will resolve to null.
118    **/

119   public ELEvaluator (VariableResolver pResolver)
120   {
121     mResolver = pResolver;
122   }
123
124   //-------------------------------------
125
/**
126    *
127    * Constructor
128    *
129    * @param pResolver the object that should be used to resolve
130    * variable names encountered in expressions. If null, all variable
131    * references will resolve to null.
132    *
133    * @param pBypassCache flag indicating if the cache should be
134    * bypassed
135    **/

136   public ELEvaluator (VariableResolver pResolver,
137               boolean pBypassCache)
138   {
139     mResolver = pResolver;
140     mBypassCache = pBypassCache;
141   }
142
143   //-------------------------------------
144
/**
145    *
146    * Evaluates the given expression String
147    *
148    * @param pExpressionString the expression String to be evaluated
149    * @param pContext the context passed to the VariableResolver for
150    * resolving variable names
151    * @param pExpectedType the type to which the evaluated expression
152    * should be coerced
153    * @return the expression String evaluated to the given expected
154    * type
155    **/

156   public Object JavaDoc evaluate (String JavaDoc pExpressionString,
157               Object JavaDoc pContext,
158               Class JavaDoc pExpectedType,
159               Map JavaDoc functions,
160               String JavaDoc defaultPrefix)
161     throws ELException
162   {
163     return evaluate (pExpressionString,
164              pContext,
165              pExpectedType,
166              functions,
167              defaultPrefix,
168              sLogger);
169   }
170
171   //-------------------------------------
172
/**
173    *
174    * Evaluates the given expression string
175    **/

176   Object JavaDoc evaluate (String JavaDoc pExpressionString,
177            Object JavaDoc pContext,
178            Class JavaDoc pExpectedType,
179            Map JavaDoc functions,
180            String JavaDoc defaultPrefix,
181            Logger pLogger)
182     throws ELException
183   {
184     // Check for null expression strings
185
if (pExpressionString == null) {
186       throw new ELException
187     (Constants.NULL_EXPRESSION_STRING);
188     }
189
190     // Get the parsed version of the expression string
191
Object JavaDoc parsedValue = parseExpressionString (pExpressionString);
192
193     // Evaluate differently based on the parsed type
194
if (parsedValue instanceof String JavaDoc) {
195       // Convert the String, and cache the conversion
196
String JavaDoc strValue = (String JavaDoc) parsedValue;
197       return convertStaticValueToExpectedType (strValue,
198                            pExpectedType,
199                            pLogger);
200     }
201
202     else if (parsedValue instanceof Expression) {
203       // Evaluate the expression and convert
204
Object JavaDoc value =
205     ((Expression) parsedValue).evaluate (pContext,
206                          mResolver,
207                          functions,
208                          defaultPrefix,
209                          pLogger);
210       return convertToExpectedType (value,
211                     pExpectedType,
212                     pLogger);
213     }
214
215     else if (parsedValue instanceof ExpressionString) {
216       // Evaluate the expression/string list and convert
217
String JavaDoc strValue =
218     ((ExpressionString) parsedValue).evaluate (pContext,
219                            mResolver,
220                            functions,
221                            defaultPrefix,
222                            pLogger);
223       return convertToExpectedType (strValue,
224                     pExpectedType,
225                     pLogger);
226     }
227
228     else {
229       // This should never be reached
230
return null;
231     }
232   }
233
234   //-------------------------------------
235
/**
236    *
237    * Gets the parsed form of the given expression string. If the
238    * parsed form is cached (and caching is not bypassed), return the
239    * cached form, otherwise parse and cache the value. Returns either
240    * a String, Expression, or ExpressionString.
241    **/

242   public Object JavaDoc parseExpressionString (String JavaDoc pExpressionString)
243     throws ELException
244   {
245     // See if it's an empty String
246
if (pExpressionString.length () == 0) {
247       return "";
248     }
249
250     // See if it's in the cache
251
Object JavaDoc ret =
252       mBypassCache ?
253       null :
254       sCachedExpressionStrings.get (pExpressionString);
255
256     if (ret == null) {
257       // Parse the expression
258
Reader JavaDoc r = new StringReader JavaDoc (pExpressionString);
259       ELParser parser = new ELParser (r);
260       try {
261     ret = parser.ExpressionString ();
262     sCachedExpressionStrings.put (pExpressionString, ret);
263       }
264       catch (ParseException exc) {
265     throw new ELException
266       (formatParseException (pExpressionString,
267                  exc));
268       }
269       catch (TokenMgrError exc) {
270     // Note - this should never be reached, since the parser is
271
// constructed to tokenize any input (illegal inputs get
272
// parsed to <BADLY_ESCAPED_STRING_LITERAL> or
273
// <ILLEGAL_CHARACTER>
274
throw new ELException (exc.getMessage ());
275       }
276     }
277     return ret;
278   }
279
280   //-------------------------------------
281
/**
282    *
283    * Converts the given value to the specified expected type.
284    **/

285   Object JavaDoc convertToExpectedType (Object JavaDoc pValue,
286                 Class JavaDoc pExpectedType,
287                 Logger pLogger)
288     throws ELException
289   {
290     return Coercions.coerce (pValue,
291                  pExpectedType,
292                  pLogger);
293   }
294
295   //-------------------------------------
296
/**
297    *
298    * Converts the given String, specified as a static expression
299    * string, to the given expected type. The conversion is cached.
300    **/

301   Object JavaDoc convertStaticValueToExpectedType (String JavaDoc pValue,
302                        Class JavaDoc pExpectedType,
303                        Logger pLogger)
304     throws ELException
305   {
306     // See if the value is already of the expected type
307
if (pExpectedType == String JavaDoc.class ||
308     pExpectedType == Object JavaDoc.class) {
309       return pValue;
310     }
311
312     // Find the cached value
313
Map JavaDoc valueByString = getOrCreateExpectedTypeMap (pExpectedType);
314     if (!mBypassCache &&
315     valueByString.containsKey (pValue)) {
316       return valueByString.get (pValue);
317     }
318     else {
319       // Convert from a String
320
Object JavaDoc ret = Coercions.coerce (pValue, pExpectedType, pLogger);
321       valueByString.put (pValue, ret);
322       return ret;
323     }
324   }
325
326   //-------------------------------------
327
/**
328    *
329    * Creates or returns the Map that maps string literals to parsed
330    * values for the specified expected type.
331    **/

332   static Map JavaDoc getOrCreateExpectedTypeMap (Class JavaDoc pExpectedType)
333   {
334     synchronized (sCachedExpectedTypes) {
335       Map JavaDoc ret = (Map JavaDoc) sCachedExpectedTypes.get (pExpectedType);
336       if (ret == null) {
337     ret = Collections.synchronizedMap (new HashMap JavaDoc ());
338     sCachedExpectedTypes.put (pExpectedType, ret);
339       }
340       return ret;
341     }
342   }
343
344   //-------------------------------------
345
// Formatting ParseException
346
//-------------------------------------
347
/**
348    *
349    * Formats a ParseException into an error message suitable for
350    * displaying on a web page
351    **/

352   static String JavaDoc formatParseException (String JavaDoc pExpressionString,
353                       ParseException pExc)
354   {
355     // Generate the String of expected tokens
356
StringBuffer JavaDoc expectedBuf = new StringBuffer JavaDoc ();
357     int maxSize = 0;
358     boolean printedOne = false;
359
360     if (pExc.expectedTokenSequences == null)
361       return pExc.toString();
362
363     for (int i = 0; i < pExc.expectedTokenSequences.length; i++) {
364       if (maxSize < pExc.expectedTokenSequences [i].length) {
365         maxSize = pExc.expectedTokenSequences [i].length;
366       }
367       for (int j = 0; j < pExc.expectedTokenSequences [i].length; j++) {
368     if (printedOne) {
369       expectedBuf.append (", ");
370     }
371         expectedBuf.append
372       (pExc.tokenImage [pExc.expectedTokenSequences [i] [j]]);
373     printedOne = true;
374       }
375     }
376     String JavaDoc expected = expectedBuf.toString ();
377
378     // Generate the String of encountered tokens
379
StringBuffer JavaDoc encounteredBuf = new StringBuffer JavaDoc ();
380     Token tok = pExc.currentToken.next;
381     for (int i = 0; i < maxSize; i++) {
382       if (i != 0) encounteredBuf.append (" ");
383       if (tok.kind == 0) {
384         encounteredBuf.append (pExc.tokenImage [0]);
385         break;
386       }
387       encounteredBuf.append (addEscapes (tok.image));
388       tok = tok.next;
389     }
390     String JavaDoc encountered = encounteredBuf.toString ();
391
392     // Format the error message
393
return MessageFormat.format
394       (Constants.PARSE_EXCEPTION,
395        new Object JavaDoc [] {
396      expected,
397      encountered,
398        });
399   }
400
401   //-------------------------------------
402
/**
403    *
404    * Used to convert raw characters to their escaped version when
405    * these raw version cannot be used as part of an ASCII string
406    * literal.
407    **/

408   static String JavaDoc addEscapes (String JavaDoc str)
409   {
410     StringBuffer JavaDoc retval = new StringBuffer JavaDoc ();
411     char ch;
412     for (int i = 0; i < str.length (); i++) {
413       switch (str.charAt (i)) {
414     case 0 :
415       continue;
416     case '\b':
417       retval.append ("\\b");
418       continue;
419     case '\t':
420       retval.append ("\\t");
421       continue;
422     case '\n':
423       retval.append ("\\n");
424       continue;
425     case '\f':
426       retval.append ("\\f");
427       continue;
428     case '\r':
429       retval.append ("\\r");
430       continue;
431     default:
432       if ((ch = str.charAt (i)) < 0x20 || ch > 0x7e) {
433         String JavaDoc s = "0000" + Integer.toString (ch, 16);
434         retval.append ("\\u" + s.substring (s.length () - 4, s.length ()));
435       }
436       else {
437         retval.append (ch);
438       }
439       continue;
440         }
441     }
442     return retval.toString ();
443   }
444
445   //-------------------------------------
446
// Testing methods
447
//-------------------------------------
448
/**
449    *
450    * Parses the given expression string, then converts it back to a
451    * String in its canonical form. This is used to test parsing.
452    **/

453   public String JavaDoc parseAndRender (String JavaDoc pExpressionString)
454     throws ELException
455   {
456     Object JavaDoc val = parseExpressionString (pExpressionString);
457     if (val instanceof String JavaDoc) {
458       return (String JavaDoc) val;
459     }
460     else if (val instanceof Expression) {
461       return "${" + ((Expression) val).getExpressionString () + "}";
462     }
463     else if (val instanceof ExpressionString) {
464       return ((ExpressionString) val).getExpressionString ();
465     }
466     else {
467       return "";
468     }
469   }
470
471   //-------------------------------------
472

473 }
474
Popular Tags