##### Coverage Report - org.apache.commons.scxml.env.jsp.ELEvaluator

Classes in this File | Line Coverage | Branch Coverage | Complexity
ELEvaluator |
52% |
89% |
| 3.8461538461538463;3.846

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