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