View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  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  package org.apache.commons.scxml.env.jexl;
    18
    19  import java.io.Serializable;
    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, Serializable {
    39
    40      /*** Serial version UID. */
    41      private static final long serialVersionUID = 1L;
    42
    43      /*** Error message if evaluation context is not a JexlContext. */
    44      private static final String ERR_CTX_TYPE = "Error evaluating JEXL "
    45          + "expression, Context must be a org.apache.commons.jexl.JexlContext";
    46
    47      /*** Pattern for recognizing the SCXML In() special predicate. */
    48      private static Pattern inFct = Pattern.compile("In//(");
    49      /*** Pattern for recognizing the Commons SCXML Data() builtin function. */
    50      private static Pattern dataFct = Pattern.compile("Data//(");
    51
    52      /*** Constructor. */
    53      public JexlEvaluator() {
    54          super();
    55      }
    56
    57      /***
    58       * Evaluate an expression.
    59       *
    60       * @param ctx variable context
    61       * @param expr expression
    62       * @return a result of the evaluation
    63       * @throws SCXMLExpressionException For a malformed expression
    64       * @see Evaluator#eval(Context, String)
    65       */
    66      public Object eval(final Context ctx, final String expr)
    67      throws SCXMLExpressionException {
    68          if (expr == null) {
    69              return null;
    70          }
    71          JexlContext jexlCtx = null;
    72          if (ctx instanceof JexlContext) {
    73              jexlCtx = (JexlContext) ctx;
    74          } else {
    75              throw new SCXMLExpressionException(ERR_CTX_TYPE);
    76          }
    77          Expression exp = null;
    78          try {
    79              String evalExpr = inFct.matcher(expr).
    80                  replaceAll("_builtin.isMember(_ALL_STATES, ");
    81              evalExpr = dataFct.matcher(evalExpr).
    82                  replaceAll("_builtin.data(_ALL_NAMESPACES, ");
    83              exp = ExpressionFactory.createExpression(evalExpr);
    84              return exp.evaluate(getEffectiveContext(jexlCtx));
    85          } catch (Exception e) {
    86              throw new SCXMLExpressionException(e);
    87          }
    88      }
    89
    90      /***
    91       * @see Evaluator#evalCond(Context, String)
    92       */
    93      public Boolean evalCond(final Context ctx, final String expr)
    94      throws SCXMLExpressionException {
    95          if (expr == null) {
    96              return null;
    97          }
    98          JexlContext jexlCtx = null;
    99          if (ctx instanceof JexlContext) {
    100             jexlCtx = (JexlContext) ctx;
    101         } else {
    102             throw new SCXMLExpressionException(ERR_CTX_TYPE);
    103         }
    104         Expression exp = null;
    105         try {
    106             String evalExpr = inFct.matcher(expr).
    107                 replaceAll("_builtin.isMember(_ALL_STATES, ");
    108             evalExpr = dataFct.matcher(evalExpr).
    109                 replaceAll("_builtin.data(_ALL_NAMESPACES, ");
    110             exp = ExpressionFactory.createExpression(evalExpr);
    111             return (Boolean) exp.evaluate(getEffectiveContext(jexlCtx));
    112         } catch (Exception e) {
    113             throw new SCXMLExpressionException(e);
    114         }
    115     }
    116
    117     /***
    118      * @see Evaluator#evalLocation(Context, String)
    119      */
    120     public Node evalLocation(final Context ctx, final String expr)
    121     throws SCXMLExpressionException {
    122         if (expr == null) {
    123             return null;
    124         }
    125         JexlContext jexlCtx = null;
    126         if (ctx instanceof JexlContext) {
    127             jexlCtx = (JexlContext) ctx;
    128         } else {
    129             throw new SCXMLExpressionException(ERR_CTX_TYPE);
    130         }
    131         Expression exp = null;
    132         try {
    133             String evalExpr = inFct.matcher(expr).
    134                 replaceAll("_builtin.isMember(_ALL_STATES, ");
    135             evalExpr = dataFct.matcher(evalExpr).
    136                 replaceFirst("_builtin.dataNode(_ALL_NAMESPACES, ");
    137             evalExpr = dataFct.matcher(evalExpr).
    138                 replaceAll("_builtin.data(_ALL_NAMESPACES, ");
    139             exp = ExpressionFactory.createExpression(evalExpr);
    140             return (Node) exp.evaluate(getEffectiveContext(jexlCtx));
    141         } catch (Exception e) {
    142             throw new SCXMLExpressionException(e);
    143         }
    144     }
    145
    146     /***
    147      * Create a new child context.
    148      *
    149      * @param parent parent context
    150      * @return new child context
    151      * @see Evaluator#newContext(Context)
    152      */
    153     public Context newContext(final Context parent) {
    154         return new JexlContext(parent);
    155     }
    156
    157     /***
    158      * Create a new context which is the summation of contexts from the
    159      * current state to document root, child has priority over parent
    160      * in scoping rules.
    161      *
    162      * @param nodeCtx The JexlContext for this state.
    163      * @return The effective JexlContext for the path leading up to
    164      *         document root.
    165      */
    166     private JexlContext getEffectiveContext(final JexlContext nodeCtx) {
    167         List contexts = new ArrayList();
    168         // trace path to root
    169         JexlContext currentCtx = nodeCtx;
    170         while (currentCtx != null) {
    171             contexts.add(currentCtx);
    172             currentCtx = (JexlContext) currentCtx.getParent();
    173         }
    174         Map vars = new HashMap();
    175         // summation of the contexts, parent first, child wins
    176         for (int i = contexts.size() - 1; i > -1; i--) {
    177             vars.putAll(((JexlContext) contexts.get(i)).getVars());
    178         }
    179         return new JexlContext(vars);
    180     }
    181
    182 }
    183