View Javadoc

    1   /*
    2    *
    3    *   Copyright 2005-2006 The Apache Software Foundation.
    4    *
    5    *  Licensed under the Apache License, Version 2.0 (the "License");
    6    *  you may not use this file except in compliance with the License.
    7    *  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   *  Unless required by applicable law or agreed to in writing, software
    12   *  distributed under the License is distributed on an "AS IS" BASIS,
    13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   *  See the License for the specific language governing permissions and
    15   *  limitations under the License.
    16   *
    17   */
    18  package org.apache.commons.scxml.env.jsp;
    19
    20  import java.lang.reflect.Method;
    21  import java.util.Set;
    22  import java.util.regex.Pattern;
    23
    24  import javax.servlet.jsp.el.ELException;
    25  import javax.servlet.jsp.el.ExpressionEvaluator;
    26  import javax.servlet.jsp.el.FunctionMapper;
    27  import javax.servlet.jsp.el.VariableResolver;
    28
    29  import org.apache.commons.el.ExpressionEvaluatorImpl;
    30  import org.apache.commons.logging.Log;
    31  import org.apache.commons.logging.LogFactory;
    32  import org.apache.commons.scxml.Builtin;
    33  import org.apache.commons.scxml.Context;
    34  import org.apache.commons.scxml.Evaluator;
    35  import org.apache.commons.scxml.SCXMLExpressionException;
    36  import org.w3c.dom.Node;
    37
    38  /***
    39   * Evaluator implementation enabling use of EL expressions in
    40   * SCXML documents.
    41   *
    42   */
    43  public class ELEvaluator implements Evaluator {
    44
    45      /*** Implementation independent log category. */
    46      private Log log = LogFactory.getLog(Evaluator.class);
    47      /*** Function Mapper for SCXML builtin functions. */
    48      private FunctionMapper builtinFnMapper = new BuiltinFunctionMapper();
    49      /*** User provided function mapper, we delegate to this mapper if
    50          we encounter a function that is not built into SCXML. */
    51      private FunctionMapper fnMapper;
    52      /*** Pattern for recognizing the SCXML In() special predicate. */
    53      private static Pattern inFct = Pattern.compile("In//(");
    54      /*** Pattern for recognizing the Commons SCXML Data() builtin function. */
    55      private static Pattern dataFct = Pattern.compile("Data//(");
    56
    57      /*** The expression evaluator implementation for the JSP/EL environment. */
    58      private ExpressionEvaluator ee = null;
    59
    60      /***
    61       * Constructor.
    62       */
    63      public ELEvaluator() {
    64          ee = new ExpressionEvaluatorImpl();
    65      }
    66
    67      /***
    68       * Constructor for EL evaluator that supports user-defined functions.
    69       *
    70       * @param fnMapper The function mapper for this Evaluator.
    71       * @see javax.servlet.jsp.el.FunctionMapper
    72       */
    73      public ELEvaluator(final FunctionMapper fnMapper) {
    74          ee = new ExpressionEvaluatorImpl();
    75          this.fnMapper = fnMapper;
    76      }
    77
    78      /***
    79       * Evaluate an expression.
    80       *
    81       * @param ctx variable context
    82       * @param expr expression
    83       * @return a result of the evaluation
    84       * @throws SCXMLExpressionException For a malformed expression
    85       * @see Evaluator#eval(Context, String)
    86       */
    87      public Object eval(final Context ctx, final String expr)
    88      throws SCXMLExpressionException {
    89          if (expr == null) {
    90              return null;
    91          }
    92          VariableResolver vr = null;
    93          if (ctx instanceof VariableResolver) {
    94              vr = (VariableResolver) ctx;
    95          } else {
    96              vr = new ContextWrapper(ctx);
    97          }
    98          try {
    99              String evalExpr = inFct.matcher(expr).
    100                 replaceAll("In(_ALL_STATES, ");
    101             Object rslt = ee.evaluate(evalExpr, Object.class, vr,
    102                 builtinFnMapper);
    103             if (log.isTraceEnabled()) {
    104                 log.trace(expr + " = " + String.valueOf(rslt));
    105             }
    106             return rslt;
    107         } catch (ELException e) {
    108             throw new SCXMLExpressionException(e);
    109         }
    110     }
    111
    112     /***
    113      * @see Evaluator#evalCond(Context, String)
    114      */
    115     public Boolean evalCond(final Context ctx, final String expr)
    116     throws SCXMLExpressionException {
    117         if (expr == null) {
    118             return null;
    119         }
    120         VariableResolver vr = null;
    121         if (ctx instanceof VariableResolver) {
    122             vr = (VariableResolver) ctx;
    123         } else {
    124             vr = new ContextWrapper(ctx);
    125         }
    126         try {
    127             String evalExpr = inFct.matcher(expr).
    128                 replaceAll("In(_ALL_STATES, ");
    129             Boolean rslt = (Boolean) ee.evaluate(evalExpr, Boolean.class,
    130                 vr, builtinFnMapper);
    131             if (log.isDebugEnabled()) {
    132                 log.debug(expr + " = " + String.valueOf(rslt));
    133             }
    134             return rslt;
    135         } catch (ELException e) {
    136             throw new SCXMLExpressionException(e);
    137         }
    138     }
    139
    140     /***
    141      * @see Evaluator#evalLocation(Context, String)
    142      */
    143     public Node evalLocation(final Context ctx, final String expr)
    144     throws SCXMLExpressionException {
    145         if (expr == null) {
    146             return null;
    147         }
    148         VariableResolver vr = null;
    149         if (ctx instanceof VariableResolver) {
    150             vr = (VariableResolver) ctx;
    151         } else {
    152             vr = new ContextWrapper(ctx);
    153         }
    154         try {
    155             String evalExpr = inFct.matcher(expr).
    156                 replaceAll("In(_ALL_STATES, ");
    157             evalExpr = dataFct.matcher(evalExpr).
    158                 replaceFirst("LData(");
    159             Node rslt = (Node) ee.evaluate(evalExpr, Node.class,
    160                 vr, builtinFnMapper);
    161             if (log.isDebugEnabled()) {
    162                 log.debug(expr + " = " + String.valueOf(rslt));
    163             }
    164             return rslt;
    165         } catch (ELException e) {
    166             throw new SCXMLExpressionException(e);
    167         }
    168     }
    169
    170     /***
    171      * Create a new child context.
    172      *
    173      * @param parent parent context
    174      * @return new child context
    175      * @see Evaluator#newContext(Context)
    176      */
    177     public Context newContext(final Context parent) {
    178         return new ELContext(parent);
    179     }
    180
    181     /***
    182      * Set the log used by this <code>Evaluator</code> instance.
    183      *
    184      * @param log The new log.
    185      */
    186     protected void setLog(final Log log) {
    187         this.log = log;
    188     }
    189
    190     /***
    191      * Get the log used by this <code>Evaluator</code> instance.
    192      *
    193      * @return Log The log being used.
    194      */
    195     protected Log getLog() {
    196         return log;
    197     }
    198
    199     /***
    200      * A Context wrapper that implements VariableResolver.
    201      */
    202     static class ContextWrapper implements VariableResolver {
    203         /*** Context to be wrapped. */
    204         private Context ctx = null;
    205         /*** The log. */
    206         private Log log = LogFactory.getLog(ContextWrapper.class);
    207         /***
    208          * Constructor.
    209          * @param ctx The Context to be wrapped.
    210          */
    211         ContextWrapper(final Context ctx) {
    212             this.ctx = ctx;
    213         }
    214         /*** @see VariableResolver#resolveVariable(String) */
    215         public Object resolveVariable(final String pName) throws ELException {
    216             Object rslt = ctx.get(pName);
    217             if (rslt == null) {
    218                 log.info("Variable \"" + pName + "\" does not exist!");
    219             }
    220             return rslt;
    221         }
    222     }
    223
    224     /***
    225      * A simple function mapper for SCXML defined functions.
    226      */
    227     class BuiltinFunctionMapper implements FunctionMapper {
    228         /*** The log. */
    229         private Log log = LogFactory.getLog(BuiltinFunctionMapper.class);
    230         /***
    231          * @see FunctionMapper#resolveFunction(String, String)
    232          */
    233         public Method resolveFunction(final String prefix,
    234                 final String localName) {
    235             if (localName.equals("In")) {
    236                 Class[] attrs = new Class[] {Set.class, String.class};
    237                 try {
    238                     return Builtin.class.getMethod("isMember", attrs);
    239                 } catch (SecurityException e) {
    240                     log.error("resolving isMember(Set, String)", e);
    241                 } catch (NoSuchMethodException e) {
    242                     log.error("resolving isMember(Set, String)", e);
    243                 }
    244             } else if (localName.equals("Data")) {
    245                 // rvalue in expressions, coerce to String
    246                 Class[] attrs = new Class[] {Object.class, String.class};
    247                 try {
    248                     return Builtin.class.getMethod("data", attrs);
    249                 } catch (SecurityException e) {
    250                     log.error("resolving data(Node, String)", e);
    251                 } catch (NoSuchMethodException e) {
    252                     log.error("resolving data(Node, String)", e);
    253                 }
    254             } else if (localName.equals("LData")) {
    255                 // lvalue in expressions, retain as Node
    256                 Class[] attrs = new Class[] {Object.class, String.class};
    257                 try {
    258                     return Builtin.class.getMethod("dataNode", attrs);
    259                 } catch (SecurityException e) {
    260                     log.error("resolving data(Node, String)", e);
    261                 } catch (NoSuchMethodException e) {
    262                     log.error("resolving data(Node, String)", e);
    263                 }
    264             } else if (fnMapper != null) {
    265                 return fnMapper.resolveFunction(prefix, localName);
    266             }
    267             return null;
    268         }
    269     }
    270
    271     /***
    272      * Get the FunctionMapper for builtin SCXML/Commons SCXML functions.
    273      *
    274      * @return builtinFnMapper The FunctionMapper
    275      */
    276     protected FunctionMapper getBuiltinFnMapper() {
    277         return builtinFnMapper;
    278     }
    279
    280 }
    281