View Javadoc

    1   /*
    2    *
    3    *   Copyright 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.jexl;
    19
    20  import java.util.ArrayList;
    21  import java.util.HashMap;
    22  import java.util.List;
    23  import java.util.Map;
    24  import java.util.regex.Pattern;
    25
    26  import org.apache.commons.jexl.Expression;
    27  import org.apache.commons.jexl.ExpressionFactory;
    28  import org.apache.commons.scxml.Context;
    29  import org.apache.commons.scxml.Evaluator;
    30  import org.apache.commons.scxml.SCXMLExpressionException;
    31  import org.w3c.dom.Node;
    32
    33  /***
    34   * Evaluator implementation enabling use of JEXL expressions in
    35   * SCXML documents.
    36   *
    37   */
    38  public class JexlEvaluator implements Evaluator {
    39
    40      /*** Error message if evaluation context is not a JexlContext. */
    41      private static final String ERR_CTX_TYPE = "Error evaluating JEXL "
    42          + "expression, Context must be a org.apache.commons.jexl.JexlContext";
    43
    44      /*** Pattern for recognizing the SCXML In() special predicate. */
    45      private static Pattern inFct = Pattern.compile("In//(");
    46      /*** Pattern for recognizing the Commons SCXML Data() builtin function. */
    47      private static Pattern dataFct = Pattern.compile("Data//(");
    48
    49      /*** Constructor. */
    50      public JexlEvaluator() {
    51          super();
    52      }
    53
    54      /***
    55       * Evaluate an expression.
    56       *
    57       * @param ctx variable context
    58       * @param expr expression
    59       * @return a result of the evaluation
    60       * @throws SCXMLExpressionException For a malformed expression
    61       * @see Evaluator#eval(Context, String)
    62       */
    63      public Object eval(final Context ctx, final String expr)
    64      throws SCXMLExpressionException {
    65          if (expr == null) {
    66              return null;
    67          }
    68          JexlContext jexlCtx = null;
    69          if (ctx instanceof JexlContext) {
    70              jexlCtx = (JexlContext) ctx;
    71          } else {
    72              throw new SCXMLExpressionException(ERR_CTX_TYPE);
    73          }
    74          Expression exp = null;
    75          try {
    76              String evalExpr = inFct.matcher(expr).
    77                  replaceAll("_builtin.isMember(_ALL_STATES, ");
    78              evalExpr = dataFct.matcher(evalExpr).
    79                  replaceAll("_builtin.data(");
    80              exp = ExpressionFactory.createExpression(evalExpr);
    81              return exp.evaluate(getEffectiveContext(jexlCtx));
    82          } catch (Exception e) {
    83              throw new SCXMLExpressionException(e);
    84          }
    85      }
    86
    87      /***
    88       * @see Evaluator#evalCond(Context, String)
    89       */
    90      public Boolean evalCond(final Context ctx, final String expr)
    91      throws SCXMLExpressionException {
    92          if (expr == null) {
    93              return null;
    94          }
    95          JexlContext jexlCtx = null;
    96          if (ctx instanceof JexlContext) {
    97              jexlCtx = (JexlContext) ctx;
    98          } else {
    99              throw new SCXMLExpressionException(ERR_CTX_TYPE);
    100         }
    101         Expression exp = null;
    102         try {
    103             String evalExpr = inFct.matcher(expr).
    104                 replaceAll("_builtin.isMember(_ALL_STATES, ");
    105             evalExpr = dataFct.matcher(evalExpr).
    106                 replaceAll("_builtin.data(");
    107             exp = ExpressionFactory.createExpression(evalExpr);
    108             return (Boolean) exp.evaluate(getEffectiveContext(jexlCtx));
    109         } catch (Exception e) {
    110             throw new SCXMLExpressionException(e);
    111         }
    112     }
    113
    114     /***
    115      * @see Evaluator#evalLocation(Context, String)
    116      */
    117     public Node evalLocation(final Context ctx, final String expr)
    118     throws SCXMLExpressionException {
    119         if (expr == null) {
    120             return null;
    121         }
    122         JexlContext jexlCtx = null;
    123         if (ctx instanceof JexlContext) {
    124             jexlCtx = (JexlContext) ctx;
    125         } else {
    126             throw new SCXMLExpressionException(ERR_CTX_TYPE);
    127         }
    128         Expression exp = null;
    129         try {
    130             String evalExpr = inFct.matcher(expr).
    131                 replaceAll("_builtin.isMember(_ALL_STATES, ");
    132             evalExpr = dataFct.matcher(evalExpr).
    133                 replaceFirst("_builtin.dataNode(");
    134             evalExpr = dataFct.matcher(evalExpr).
    135                 replaceAll("_builtin.data(");
    136             exp = ExpressionFactory.createExpression(evalExpr);
    137             return (Node) exp.evaluate(getEffectiveContext(jexlCtx));
    138         } catch (Exception e) {
    139             throw new SCXMLExpressionException(e);
    140         }
    141     }
    142
    143     /***
    144      * Create a new child context.
    145      *
    146      * @param parent parent context
    147      * @return new child context
    148      * @see Evaluator#newContext(Context)
    149      */
    150     public Context newContext(final Context parent) {
    151         return new JexlContext(parent);
    152     }
    153
    154     /***
    155      * Create a new context which is the summation of contexts from the
    156      * current state to document root, child has priority over parent
    157      * in scoping rules.
    158      *
    159      * @param nodeCtx The JexlContext for this state.
    160      * @return The effective JexlContext for the path leading up to
    161      *         document root.
    162      */
    163     private JexlContext getEffectiveContext(final JexlContext nodeCtx) {
    164         List contexts = new ArrayList();
    165         // trace path to root
    166         JexlContext currentCtx = nodeCtx;
    167         while (currentCtx != null) {
    168             contexts.add(currentCtx);
    169             currentCtx = (JexlContext) currentCtx.getParent();
    170         }
    171         Map vars = new HashMap();
    172         // summation of the contexts, parent first, child wins
    173         for (int i = contexts.size() - 1; i > -1; i--) {
    174             vars.putAll(((JexlContext) contexts.get(i)).getVars());
    175         }
    176         return new JexlContext(vars);
    177     }
    178
    179 }
    180