1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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("eval('" + expr + "'):"
87 + e.getMessage(), e);
88 }
89 }
90
91 /***
92 * @see Evaluator#evalCond(Context, String)
93 */
94 public Boolean evalCond(final Context ctx, final String expr)
95 throws SCXMLExpressionException {
96 if (expr == null) {
97 return null;
98 }
99 JexlContext jexlCtx = null;
100 if (ctx instanceof JexlContext) {
101 jexlCtx = (JexlContext) ctx;
102 } else {
103 throw new SCXMLExpressionException(ERR_CTX_TYPE);
104 }
105 Expression exp = null;
106 try {
107 String evalExpr = inFct.matcher(expr).
108 replaceAll("_builtin.isMember(_ALL_STATES, ");
109 evalExpr = dataFct.matcher(evalExpr).
110 replaceAll("_builtin.data(_ALL_NAMESPACES, ");
111 exp = ExpressionFactory.createExpression(evalExpr);
112 return (Boolean) exp.evaluate(getEffectiveContext(jexlCtx));
113 } catch (Exception e) {
114 throw new SCXMLExpressionException("eval('" + expr + "'):"
115 + e.getMessage(), e);
116 }
117 }
118
119 /***
120 * @see Evaluator#evalLocation(Context, String)
121 */
122 public Node evalLocation(final Context ctx, final String expr)
123 throws SCXMLExpressionException {
124 if (expr == null) {
125 return null;
126 }
127 JexlContext jexlCtx = null;
128 if (ctx instanceof JexlContext) {
129 jexlCtx = (JexlContext) ctx;
130 } else {
131 throw new SCXMLExpressionException(ERR_CTX_TYPE);
132 }
133 Expression exp = null;
134 try {
135 String evalExpr = inFct.matcher(expr).
136 replaceAll("_builtin.isMember(_ALL_STATES, ");
137 evalExpr = dataFct.matcher(evalExpr).
138 replaceFirst("_builtin.dataNode(_ALL_NAMESPACES, ");
139 evalExpr = dataFct.matcher(evalExpr).
140 replaceAll("_builtin.data(_ALL_NAMESPACES, ");
141 exp = ExpressionFactory.createExpression(evalExpr);
142 return (Node) exp.evaluate(getEffectiveContext(jexlCtx));
143 } catch (Exception e) {
144 throw new SCXMLExpressionException("eval('" + expr + "'):"
145 + e.getMessage(), e);
146 }
147 }
148
149 /***
150 * Create a new child context.
151 *
152 * @param parent parent context
153 * @return new child context
154 * @see Evaluator#newContext(Context)
155 */
156 public Context newContext(final Context parent) {
157 return new JexlContext(parent);
158 }
159
160 /***
161 * Create a new context which is the summation of contexts from the
162 * current state to document root, child has priority over parent
163 * in scoping rules.
164 *
165 * @param nodeCtx The JexlContext for this state.
166 * @return The effective JexlContext for the path leading up to
167 * document root.
168 */
169 private JexlContext getEffectiveContext(final JexlContext nodeCtx) {
170 List contexts = new ArrayList();
171
172 JexlContext currentCtx = nodeCtx;
173 while (currentCtx != null) {
174 contexts.add(currentCtx);
175 currentCtx = (JexlContext) currentCtx.getParent();
176 }
177 Map vars = new HashMap();
178
179 for (int i = contexts.size() - 1; i > -1; i--) {
180 vars.putAll(((JexlContext) contexts.get(i)).getVars());
181 }
182 return new JexlContext(vars);
183 }
184
185 }
186