1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.scxml.env.jsp;
19
20 import java.lang.reflect.Method;
21 import java.util.Set;
22 import java.util.regex.Pattern;
23
24 import javax.servlet.jsp.el.ELException;
25 import javax.servlet.jsp.el.ExpressionEvaluator;
26 import javax.servlet.jsp.el.FunctionMapper;
27 import javax.servlet.jsp.el.VariableResolver;
28
29 import org.apache.commons.el.ExpressionEvaluatorImpl;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.commons.scxml.Builtin;
33 import org.apache.commons.scxml.Context;
34 import org.apache.commons.scxml.Evaluator;
35 import org.apache.commons.scxml.SCXMLExpressionException;
36 import org.w3c.dom.Node;
37
38 /***
39 * Evaluator implementation enabling use of EL expressions in
40 * SCXML documents.
41 *
42 */
43 public class ELEvaluator implements Evaluator {
44
45 /*** Implementation independent log category. */
46 private Log log = LogFactory.getLog(Evaluator.class);
47 /*** Function Mapper for SCXML builtin functions. */
48 private FunctionMapper builtinFnMapper = new BuiltinFunctionMapper();
49 /*** User provided function mapper, we delegate to this mapper if
50 we encounter a function that is not built into SCXML. */
51 private FunctionMapper fnMapper;
52 /*** Pattern for recognizing the SCXML In() special predicate. */
53 private static Pattern inFct = Pattern.compile("In//(");
54 /*** Pattern for recognizing the Commons SCXML Data() builtin function. */
55 private static Pattern dataFct = Pattern.compile("Data//(");
56
57 /*** The expression evaluator implementation for the JSP/EL environment. */
58 private ExpressionEvaluator ee = null;
59
60 /***
61 * Constructor.
62 */
63 public ELEvaluator() {
64 ee = new ExpressionEvaluatorImpl();
65 }
66
67 /***
68 * Constructor for EL evaluator that supports user-defined functions.
69 *
70 * @param fnMapper The function mapper for this Evaluator.
71 * @see javax.servlet.jsp.el.FunctionMapper
72 */
73 public ELEvaluator(final FunctionMapper fnMapper) {
74 ee = new ExpressionEvaluatorImpl();
75 this.fnMapper = fnMapper;
76 }
77
78 /***
79 * Evaluate an expression.
80 *
81 * @param ctx variable context
82 * @param expr expression
83 * @return a result of the evaluation
84 * @throws SCXMLExpressionException For a malformed expression
85 * @see Evaluator#eval(Context, String)
86 */
87 public Object eval(final Context ctx, final String expr)
88 throws SCXMLExpressionException {
89 if (expr == null) {
90 return null;
91 }
92 VariableResolver vr = null;
93 if (ctx instanceof VariableResolver) {
94 vr = (VariableResolver) ctx;
95 } else {
96 vr = new ContextWrapper(ctx);
97 }
98 try {
99 String evalExpr = inFct.matcher(expr).
100 replaceAll("In(_ALL_STATES, ");
101 Object rslt = ee.evaluate(evalExpr, Object.class, vr,
102 builtinFnMapper);
103 if (log.isTraceEnabled()) {
104 log.trace(expr + " = " + String.valueOf(rslt));
105 }
106 return rslt;
107 } catch (ELException e) {
108 throw new SCXMLExpressionException(e);
109 }
110 }
111
112 /***
113 * @see Evaluator#evalCond(Context, String)
114 */
115 public Boolean evalCond(final Context ctx, final String expr)
116 throws SCXMLExpressionException {
117 if (expr == null) {
118 return null;
119 }
120 VariableResolver vr = null;
121 if (ctx instanceof VariableResolver) {
122 vr = (VariableResolver) ctx;
123 } else {
124 vr = new ContextWrapper(ctx);
125 }
126 try {
127 String evalExpr = inFct.matcher(expr).
128 replaceAll("In(_ALL_STATES, ");
129 Boolean rslt = (Boolean) ee.evaluate(evalExpr, Boolean.class,
130 vr, builtinFnMapper);
131 if (log.isDebugEnabled()) {
132 log.debug(expr + " = " + String.valueOf(rslt));
133 }
134 return rslt;
135 } catch (ELException e) {
136 throw new SCXMLExpressionException(e);
137 }
138 }
139
140 /***
141 * @see Evaluator#evalLocation(Context, String)
142 */
143 public Node evalLocation(final Context ctx, final String expr)
144 throws SCXMLExpressionException {
145 if (expr == null) {
146 return null;
147 }
148 VariableResolver vr = null;
149 if (ctx instanceof VariableResolver) {
150 vr = (VariableResolver) ctx;
151 } else {
152 vr = new ContextWrapper(ctx);
153 }
154 try {
155 String evalExpr = inFct.matcher(expr).
156 replaceAll("In(_ALL_STATES, ");
157 evalExpr = dataFct.matcher(evalExpr).
158 replaceFirst("LData(");
159 Node rslt = (Node) ee.evaluate(evalExpr, Node.class,
160 vr, builtinFnMapper);
161 if (log.isDebugEnabled()) {
162 log.debug(expr + " = " + String.valueOf(rslt));
163 }
164 return rslt;
165 } catch (ELException e) {
166 throw new SCXMLExpressionException(e);
167 }
168 }
169
170 /***
171 * Create a new child context.
172 *
173 * @param parent parent context
174 * @return new child context
175 * @see Evaluator#newContext(Context)
176 */
177 public Context newContext(final Context parent) {
178 return new ELContext(parent);
179 }
180
181 /***
182 * Set the log used by this <code>Evaluator</code> instance.
183 *
184 * @param log The new log.
185 */
186 protected void setLog(final Log log) {
187 this.log = log;
188 }
189
190 /***
191 * Get the log used by this <code>Evaluator</code> instance.
192 *
193 * @return Log The log being used.
194 */
195 protected Log getLog() {
196 return log;
197 }
198
199 /***
200 * A Context wrapper that implements VariableResolver.
201 */
202 static class ContextWrapper implements VariableResolver {
203 /*** Context to be wrapped. */
204 private Context ctx = null;
205 /*** The log. */
206 private Log log = LogFactory.getLog(ContextWrapper.class);
207 /***
208 * Constructor.
209 * @param ctx The Context to be wrapped.
210 */
211 ContextWrapper(final Context ctx) {
212 this.ctx = ctx;
213 }
214 /*** @see VariableResolver#resolveVariable(String) */
215 public Object resolveVariable(final String pName) throws ELException {
216 Object rslt = ctx.get(pName);
217 if (rslt == null) {
218 log.info("Variable \"" + pName + "\" does not exist!");
219 }
220 return rslt;
221 }
222 }
223
224 /***
225 * A simple function mapper for SCXML defined functions.
226 */
227 class BuiltinFunctionMapper implements FunctionMapper {
228 /*** The log. */
229 private Log log = LogFactory.getLog(BuiltinFunctionMapper.class);
230 /***
231 * @see FunctionMapper#resolveFunction(String, String)
232 */
233 public Method resolveFunction(final String prefix,
234 final String localName) {
235 if (localName.equals("In")) {
236 Class[] attrs = new Class[] {Set.class, String.class};
237 try {
238 return Builtin.class.getMethod("isMember", attrs);
239 } catch (SecurityException e) {
240 log.error("resolving isMember(Set, String)", e);
241 } catch (NoSuchMethodException e) {
242 log.error("resolving isMember(Set, String)", e);
243 }
244 } else if (localName.equals("Data")) {
245
246 Class[] attrs = new Class[] {Object.class, String.class};
247 try {
248 return Builtin.class.getMethod("data", attrs);
249 } catch (SecurityException e) {
250 log.error("resolving data(Node, String)", e);
251 } catch (NoSuchMethodException e) {
252 log.error("resolving data(Node, String)", e);
253 }
254 } else if (localName.equals("LData")) {
255
256 Class[] attrs = new Class[] {Object.class, String.class};
257 try {
258 return Builtin.class.getMethod("dataNode", attrs);
259 } catch (SecurityException e) {
260 log.error("resolving data(Node, String)", e);
261 } catch (NoSuchMethodException e) {
262 log.error("resolving data(Node, String)", e);
263 }
264 } else if (fnMapper != null) {
265 return fnMapper.resolveFunction(prefix, localName);
266 }
267 return null;
268 }
269 }
270
271 /***
272 * Get the FunctionMapper for builtin SCXML/Commons SCXML functions.
273 *
274 * @return builtinFnMapper The FunctionMapper
275 */
276 protected FunctionMapper getBuiltinFnMapper() {
277 return builtinFnMapper;
278 }
279
280 }
281