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(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
169 JexlContext currentCtx = nodeCtx;
170 while (currentCtx != null) {
171 contexts.add(currentCtx);
172 currentCtx = (JexlContext) currentCtx.getParent();
173 }
174 Map vars = new HashMap();
175
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