1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
166 JexlContext currentCtx = nodeCtx;
167 while (currentCtx != null) {
168 contexts.add(currentCtx);
169 currentCtx = (JexlContext) currentCtx.getParent();
170 }
171 Map vars = new HashMap();
172
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