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

Classes in this File | Line Coverage | Branch Coverage | Complexity
ELEvaluator |
49% |
88% |
| 4.0;4

1 |  |
     /*
2 |  |
      *
3 |  |
      *   Copyright 2005-2006 The Apache Software Foundation.
4 |  |
      *
5 |  |
      *  Licensed under the Apache License, Version 2.0 (the "License");
6 |  |
      *  you may not use this file except in compliance with the License.
7 |  |
      *  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 |  |
      */
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 | 0 |
     public class ELEvaluator implements Evaluator {
44 |  |
45 |  |
         /** Implementation independent log category. */
46 | 28 |
         private Log log = LogFactory.getLog(Evaluator.class);
47 |  |
         /** Function Mapper for SCXML builtin functions. */
48 | 11 |
         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 | 3 |
         private static Pattern inFct = Pattern.compile("In\\(");
54 |  |
         /** Pattern for recognizing the Commons SCXML Data() builtin function. */
55 | 3 |
         private static Pattern dataFct = Pattern.compile("Data\\(");
56 |  |
57 |  |
         /** The expression evaluator implementation for the JSP/EL environment. */
58 | 11 |
         private ExpressionEvaluator ee = null;
59 |  |
60 |  |
         /**
61 |  |
          * Constructor.
62 |  |
          */
63 | 11 |
         public ELEvaluator() {
64 | 11 |
             ee = new ExpressionEvaluatorImpl();
65 | 11 |
         }
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 | 0 |
         public ELEvaluator(final FunctionMapper fnMapper) {
74 | 0 |
             ee = new ExpressionEvaluatorImpl();
75 | 0 |
             this.fnMapper = fnMapper;
76 | 0 |
         }
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 | 34 |
             if (expr == null) {
90 | 0 |
                 return null;
91 |  |
             }
92 | 34 |
             VariableResolver vr = null;
93 | 34 |
             if (ctx instanceof VariableResolver) {
94 | 34 |
                 vr = (VariableResolver) ctx;
95 |  |
             } else {
96 | 0 |
                 vr = new ContextWrapper(ctx);
97 |  |
             }
98 |  |
             try {
99 | 34 |
                 String evalExpr = inFct.matcher(expr).
100 |  |
                     replaceAll("In(_ALL_STATES, ");
101 | 34 |
                 Object rslt = ee.evaluate(evalExpr, Object.class, vr,
102 |  |
                     builtinFnMapper);
103 | 34 |
                 if (log.isTraceEnabled()) {
104 | 0 |
                     log.trace(expr + " = " + String.valueOf(rslt));
105 |  |
                 }
106 | 34 |
                 return rslt;
107 | 0 |
             } catch (ELException e) {
108 | 0 |
                 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 | 28 |
             if (expr == null) {
118 | 0 |
                 return null;
119 |  |
             }
120 | 28 |
             VariableResolver vr = null;
121 | 28 |
             if (ctx instanceof VariableResolver) {
122 | 28 |
                 vr = (VariableResolver) ctx;
123 |  |
             } else {
124 | 0 |
                 vr = new ContextWrapper(ctx);
125 |  |
             }
126 |  |
             try {
127 | 28 |
                 String evalExpr = inFct.matcher(expr).
128 |  |
                     replaceAll("In(_ALL_STATES, ");
129 | 28 |
                 Boolean rslt = (Boolean) ee.evaluate(evalExpr, Boolean.class,
130 |  |
                     vr, builtinFnMapper);
131 | 28 |
                 if (log.isDebugEnabled()) {
132 | 0 |
                     log.debug(expr + " = " + String.valueOf(rslt));
133 |  |
                 }
134 | 28 |
                 return rslt;
135 | 0 |
             } catch (ELException e) {
136 | 0 |
                 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 | 12 |
             if (expr == null) {
146 | 0 |
                 return null;
147 |  |
             }
148 | 12 |
             VariableResolver vr = null;
149 | 12 |
             if (ctx instanceof VariableResolver) {
150 | 12 |
                 vr = (VariableResolver) ctx;
151 |  |
             } else {
152 | 0 |
                 vr = new ContextWrapper(ctx);
153 |  |
             }
154 |  |
             try {
155 | 12 |
                 String evalExpr = inFct.matcher(expr).
156 |  |
                     replaceAll("In(_ALL_STATES, ");
157 | 12 |
                 evalExpr = dataFct.matcher(evalExpr).
158 |  |
                     replaceFirst("LData(");
159 | 12 |
                 Node rslt = (Node) ee.evaluate(evalExpr, Node.class,
160 |  |
                     vr, builtinFnMapper);
161 | 8 |
                 if (log.isDebugEnabled()) {
162 | 0 |
                     log.debug(expr + " = " + String.valueOf(rslt));
163 |  |
                 }
164 | 8 |
                 return rslt;
165 | 4 |
             } catch (ELException e) {
166 | 4 |
                 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 | 45 |
             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 | 0 |
             this.log = log;
188 | 0 |
         }
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 | 0 |
             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 | 0 |
             private Context ctx = null;
205 |  |
             /** The log. */
206 | 0 |
             private Log log = LogFactory.getLog(ContextWrapper.class);
207 |  |
             /**
208 |  |
              * Constructor.
209 |  |
              * @param ctx The Context to be wrapped.
210 |  |
              */
211 | 0 |
             ContextWrapper(final Context ctx) {
212 | 0 |
                 this.ctx = ctx;
213 | 0 |
             }
214 |  |
             /** @see VariableResolver#resolveVariable(String) */
215 |  |
             public Object resolveVariable(final String pName) throws ELException {
216 | 0 |
                 Object rslt = ctx.get(pName);
217 | 0 |
                 if (rslt == null) {
218 | 0 |
                     log.info("Variable \"" + pName + "\" does not exist!");
219 |  |
                 }
220 | 0 |
                 return rslt;
221 |  |
             }
222 |  |
         }
223 |  |
224 |  |
         /**
225 |  |
          * A simple function mapper for SCXML defined functions.
226 |  |
          */
227 | 11 |
         class BuiltinFunctionMapper implements FunctionMapper {
228 |  |
             /** The log. */
229 | 11 |
             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 | 16 |
                 if (localName.equals("In")) {
236 | 2 |
                     Class[] attrs = new Class[] {Set.class, String.class};
237 |  |
                     try {
238 | 2 |
                         return Builtin.class.getMethod("isMember", attrs);
239 | 0 |
                     } catch (SecurityException e) {
240 | 0 |
                         log.error("resolving isMember(Set, String)", e);
241 | 0 |
                     } catch (NoSuchMethodException e) {
242 | 0 |
                         log.error("resolving isMember(Set, String)", e);
243 | 0 |
                     }
244 | 14 |
                 } else if (localName.equals("Data")) {
245 |  |
                     // rvalue in expressions, coerce to String
246 | 6 |
                     Class[] attrs = new Class[] {Object.class, String.class};
247 |  |
                     try {
248 | 6 |
                         return Builtin.class.getMethod("data", attrs);
249 | 0 |
                     } catch (SecurityException e) {
250 | 0 |
                         log.error("resolving data(Node, String)", e);
251 | 0 |
                     } catch (NoSuchMethodException e) {
252 | 0 |
                         log.error("resolving data(Node, String)", e);
253 | 0 |
                     }
254 | 8 |
                 } else if (localName.equals("LData")) {
255 |  |
                     // lvalue in expressions, retain as Node
256 | 8 |
                     Class[] attrs = new Class[] {Object.class, String.class};
257 |  |
                     try {
258 | 8 |
                         return Builtin.class.getMethod("dataNode", attrs);
259 | 0 |
                     } catch (SecurityException e) {
260 | 0 |
                         log.error("resolving data(Node, String)", e);
261 | 0 |
                     } catch (NoSuchMethodException e) {
262 | 0 |
                         log.error("resolving data(Node, String)", e);
263 | 0 |
                     }
264 | 0 |
                 } else if (fnMapper != null) {
265 | 0 |
                     return fnMapper.resolveFunction(prefix, localName);
266 |  |
                 }
267 | 0 |
                 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 | 0 |
             return builtinFnMapper;
278 |  |
         }
279 |  |
280 |  |
     }
281 |  |