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