Package org.apache.commons.el

Source Code of org.apache.commons.el.ExpressionEvaluatorImpl$JSTLExpression

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

package org.apache.commons.el;

import java.io.Reader;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.jsp.el.ExpressionEvaluator;
import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.VariableResolver;
import javax.servlet.jsp.el.FunctionMapper;
import org.apache.commons.el.parser.ELParser;
import org.apache.commons.el.parser.ParseException;
import org.apache.commons.el.parser.Token;
import org.apache.commons.el.parser.TokenMgrError;

/**
*
* <p>This is the main class for evaluating expression Strings.  An
* expression String is a String that may contain expressions of the
* form ${...}.  Multiple expressions may appear in the same
* expression String.  In such a case, the expression String's value
* is computed by concatenating the String values of those evaluated
* expressions and any intervening non-expression text, then
* converting the resulting String to the expected type using the
* PropertyEditor mechanism.
*
* <p>In the special case where the expression String is a single
* expression, the value of the expression String is determined by
* evaluating the expression, without any intervening conversion to a
* String.
*
* <p>The evaluator maintains a cache mapping expression Strings to
* their parsed results.  For expression Strings containing no
* expression elements, it maintains a cache mapping
* ExpectedType/ExpressionString to parsed value, so that static
* expression Strings won't have to go through a conversion step every
* time they are used.  All instances of the evaluator share the same
* cache.  The cache may be bypassed by setting a flag on the
* evaluator's constructor.
*
* <p>The evaluator must be passed a VariableResolver in its
* constructor.  The VariableResolver is used to resolve variable
* names encountered in expressions, and can also be used to implement
* "implicit objects" that are always present in the namespace.
* Different applications will have different policies for variable
* lookups and implicit objects - these differences can be
* encapsulated in the VariableResolver passed to the evaluator's
* constructor.
*
* <p>Most VariableResolvers will need to perform their resolution
* against some context.  For example, a JSP environment needs a
* PageContext to resolve variables.  The evaluate() method takes a
* generic Object context which is eventually passed to the
* VariableResolver - the VariableResolver is responsible for casting
* the context to the proper type.
*
* <p>Once an evaluator instance has been constructed, it may be used
* multiple times, and may be used by multiple simultaneous Threads.
* In other words, an evaluator instance is well-suited for use as a
* singleton.
*
* @author Nathan Abramson - Art Technology Group
* @author Shawn Bayern
* @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: luehe $
**/

public class ExpressionEvaluatorImpl
  extends ExpressionEvaluator
{
  //-------------------------------------
  // Properties
  //-------------------------------------

  //-------------------------------------
  // Member variables
  //-------------------------------------

  /** The mapping from expression String to its parsed form (String,
      Expression, or ExpressionString) **/
  static Map sCachedExpressionStrings =
    Collections.synchronizedMap (new HashMap ());

  /** The mapping from ExpectedType to Maps mapping literal String to
      parsed value **/
  static Map sCachedExpectedTypes = new HashMap ();

  /** The static Logger **/
  static Logger sLogger = new Logger (System.out);

  /** Flag if the cache should be bypassed **/
  boolean mBypassCache;

  //-------------------------------------
  /**
   *
   * Constructor
   **/
  public ExpressionEvaluatorImpl () { }

  /**
   *
   * Constructor
   *
   * @param pBypassCache flag indicating if the cache should be
   * bypassed
   **/
  public ExpressionEvaluatorImpl (boolean pBypassCache)
  {
    mBypassCache = pBypassCache;
  }

  //-------------------------------------
  /**
   *
   * Evaluates the given expression String
   *
   * @param pExpressionString The expression to be evaluated.
   * @param pExpectedType The expected type of the result of the evaluation
   * @param pResolver A VariableResolver instance that can be used at
   *     runtime to resolve the name of implicit objects into Objects.
   * @param functions A FunctionMapper to resolve functions found in
   *     the expression.  It can be null, in which case no functions
   *     are supported for this invocation.
   * @return the expression String evaluated to the given expected
   * type
   **/
  public Object evaluate (String pExpressionString,
        Class pExpectedType,
                          VariableResolver pResolver,
        FunctionMapper functions)
    throws ELException
  {
    return evaluate (pExpressionString,
         pExpectedType,
                     pResolver,
         functions,
         sLogger);
  }

  /**
   *
   * Prepare an expression for later evaluation.  This method should perform
   * syntactic validation of the expression; if in doing so it detects
   * errors, it should raise an ELParseException.
   *
   * @param expression The expression to be evaluated.
   * @param expectedType The expected type of the result of the evaluation
   * @param fMapper A FunctionMapper to resolve functions found in
   *     the expression.  It can be null, in which case no functions
   *     are supported for this invocation.  The ExpressionEvaluator
   *     must not hold on to the FunctionMapper reference after
   *     returning from <code>parseExpression()</code>.  The
   *     <code>Expression</code> object returned must invoke the same
   *     functions regardless of whether the mappings in the
   *     provided <code>FunctionMapper</code> instance change between
   *     calling <code>ExpressionEvaluator.parseExpression()</code>
   *     and <code>Expression.evaluate()</code>.
   * @return The Expression object encapsulating the arguments.
   *
   * @exception ELException Thrown if parsing errors were found.
   **/
  public javax.servlet.jsp.el.Expression parseExpression(String expression,
               Class expectedType,
               FunctionMapper fMapper)
    throws ELException
  {
    // Validate and then create an Expression object.
    parseExpressionString(expression);
       
    // Create an Expression object that knows how to evaluate this.
    return new JSTLExpression(this, expression, expectedType, fMapper);
  }

  //-------------------------------------
  /**
   *
   * Evaluates the given expression string
   **/
  Object evaluate (String pExpressionString,
       Class pExpectedType,
       VariableResolver pResolver,
       FunctionMapper functions,
       Logger pLogger)
    throws ELException
  {
    // Check for null expression strings
    if (pExpressionString == null) {
      throw new ELException
  (Constants.NULL_EXPRESSION_STRING);
    }

    // Get the parsed version of the expression string
    Object parsedValue = parseExpressionString (pExpressionString);

    // Evaluate differently based on the parsed type
    if (parsedValue instanceof String) {
      // Convert the String, and cache the conversion
      String strValue = (String) parsedValue;
      return convertStaticValueToExpectedType (strValue,
                 pExpectedType,
                 pLogger);
    }

    else if (parsedValue instanceof Expression) {
      // Evaluate the expression and convert
      Object value =
  ((Expression) parsedValue).evaluate (pResolver,
               functions,
               pLogger);
      return convertToExpectedType (value,
            pExpectedType,
            pLogger);
    }

    else if (parsedValue instanceof ExpressionString) {
      // Evaluate the expression/string list and convert
      String strValue =
  ((ExpressionString) parsedValue).evaluate (pResolver,
               functions,
               pLogger);
      return convertToExpectedType (strValue,
            pExpectedType,
            pLogger);
    }

    else {
      // This should never be reached
      return null;
    }
  }

  //-------------------------------------
  /**
   *
   * Gets the parsed form of the given expression string.  If the
   * parsed form is cached (and caching is not bypassed), return the
   * cached form, otherwise parse and cache the value.  Returns either
   * a String, Expression, or ExpressionString.
   **/
  public Object parseExpressionString (String pExpressionString)
    throws ELException
  {
    // See if it's an empty String
    if (pExpressionString.length () == 0) {
      return "";
    }

    // See if it's in the cache
    Object ret =
      mBypassCache ?
      null :
      sCachedExpressionStrings.get (pExpressionString);

    if (ret == null) {
      // Parse the expression
      Reader r = new StringReader (pExpressionString);
      ELParser parser = new ELParser (r);
      try {
  ret = parser.ExpressionString ();
  sCachedExpressionStrings.put (pExpressionString, ret);
      }
      catch (ParseException exc) {
  throw new ELException
    (formatParseException (pExpressionString,
         exc));
      }
      catch (TokenMgrError exc) {
  // Note - this should never be reached, since the parser is
  // constructed to tokenize any input (illegal inputs get
  // parsed to <BADLY_ESCAPED_STRING_LITERAL> or
  // <ILLEGAL_CHARACTER>
  throw new ELException (exc.getMessage ());
      }
    }
    return ret;
  }

  //-------------------------------------
  /**
   *
   * Converts the given value to the specified expected type.
   **/
  Object convertToExpectedType (Object pValue,
        Class pExpectedType,
        Logger pLogger)
    throws ELException
  {
    return Coercions.coerce (pValue,
           pExpectedType,
           pLogger);
  }

  //-------------------------------------
  /**
   *
   * Converts the given String, specified as a static expression
   * string, to the given expected type.  The conversion is cached.
   **/
  Object convertStaticValueToExpectedType (String pValue,
             Class pExpectedType,
             Logger pLogger)
    throws ELException
  {
    // See if the value is already of the expected type
    if (pExpectedType == String.class ||
  pExpectedType == Object.class) {
      return pValue;
    }

    // Find the cached value
    Map valueByString = getOrCreateExpectedTypeMap (pExpectedType);
    if (!mBypassCache &&
  valueByString.containsKey (pValue)) {
      return valueByString.get (pValue);
    }
    else {
      // Convert from a String
      Object ret = Coercions.coerce (pValue, pExpectedType, pLogger);
      valueByString.put (pValue, ret);
      return ret;
    }
  }

  //-------------------------------------
  /**
   *
   * Creates or returns the Map that maps string literals to parsed
   * values for the specified expected type.
   **/
  static Map getOrCreateExpectedTypeMap (Class pExpectedType)
  {
    synchronized (sCachedExpectedTypes) {
      Map ret = (Map) sCachedExpectedTypes.get (pExpectedType);
      if (ret == null) {
  ret = Collections.synchronizedMap (new HashMap ());
  sCachedExpectedTypes.put (pExpectedType, ret);
      }
      return ret;
    }
  }

  //-------------------------------------
  // Formatting ParseException
  //-------------------------------------
  /**
   *
   * Formats a ParseException into an error message suitable for
   * displaying on a web page
   **/
  static String formatParseException (String pExpressionString,
              ParseException pExc)
  {
    // Generate the String of expected tokens
    StringBuffer expectedBuf = new StringBuffer ();
    int maxSize = 0;
    boolean printedOne = false;

    if (pExc.expectedTokenSequences == null)
      return pExc.toString();

    for (int i = 0; i < pExc.expectedTokenSequences.length; i++) {
      if (maxSize < pExc.expectedTokenSequences [i].length) {
        maxSize = pExc.expectedTokenSequences [i].length;
      }
      for (int j = 0; j < pExc.expectedTokenSequences [i].length; j++) {
  if (printedOne) {
    expectedBuf.append (", ");
  }
        expectedBuf.append
    (pExc.tokenImage [pExc.expectedTokenSequences [i] [j]]);
  printedOne = true;
      }
    }
    String expected = expectedBuf.toString ();

    // Generate the String of encountered tokens
    StringBuffer encounteredBuf = new StringBuffer ();
    Token tok = pExc.currentToken.next;
    for (int i = 0; i < maxSize; i++) {
      if (i != 0) encounteredBuf.append (" ");
      if (tok.kind == 0) {
        encounteredBuf.append (pExc.tokenImage [0]);
        break;
      }
      encounteredBuf.append (addEscapes (tok.image));
      tok = tok.next;
    }
    String encountered = encounteredBuf.toString ();

    // Format the error message
    return MessageFormat.format
      (Constants.PARSE_EXCEPTION,
       new Object [] {
   expected,
   encountered,
       });
  }

  //-------------------------------------
  /**
   *
   * Used to convert raw characters to their escaped version when
   * these raw version cannot be used as part of an ASCII string
   * literal.
   **/
  static String addEscapes (String str)
  {
    StringBuffer retval = new StringBuffer ();
    char ch;
    for (int i = 0; i < str.length (); i++) {
      switch (str.charAt (i)) {
  case 0 :
    continue;
  case '\b':
    retval.append ("\\b");
    continue;
  case '\t':
    retval.append ("\\t");
    continue;
  case '\n':
    retval.append ("\\n");
    continue;
  case '\f':
    retval.append ("\\f");
    continue;
  case '\r':
    retval.append ("\\r");
    continue;
  default:
    if ((ch = str.charAt (i)) < 0x20 || ch > 0x7e) {
      String s = "0000" + Integer.toString (ch, 16);
      retval.append ("\\u" + s.substring (s.length () - 4, s.length ()));
    }
    else {
      retval.append (ch);
    }
    continue;
        }
    }
    return retval.toString ();
  }

  //-------------------------------------
  // Testing methods
  //-------------------------------------
  /**
   *
   * Parses the given expression string, then converts it back to a
   * String in its canonical form.  This is used to test parsing.
   **/
  public String parseAndRender (String pExpressionString)
    throws ELException
  {
    Object val = parseExpressionString (pExpressionString);
    if (val instanceof String) {
      return (String) val;
    }
    else if (val instanceof Expression) {
      return "${" + ((Expression) val).getExpressionString () + "}";
    }
    else if (val instanceof ExpressionString) {
      return ((ExpressionString) val).getExpressionString ();
    }
    else {
      return "";
    }
  }

  /**
   * An object that encapsulates an expression to be evaluated by
   * the JSTL evaluator.
   */
  private class JSTLExpression
    extends javax.servlet.jsp.el.Expression
  {
    private ExpressionEvaluatorImpl evaluator;
    private String expression;
    private Class expectedType;
    private FunctionMapper fMapper;

    public JSTLExpression(ExpressionEvaluatorImpl evaluator, String expression,
        Class expectedType, FunctionMapper fMapper)
    {
      this.evaluator = evaluator;
      this.expression = expression;
      this.expectedType = expectedType;
      this.fMapper = fMapper;
    }
       
    public Object evaluate( VariableResolver vResolver )
      throws ELException
    {
      return evaluator.evaluate(this.expression,
        this.expectedType,
        vResolver,
        this.fMapper);
    }
  }

  //-------------------------------------

}
TOP

Related Classes of org.apache.commons.el.ExpressionEvaluatorImpl$JSTLExpression

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.