View Javadoc

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  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                 // rvalue in expressions, coerce to String
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                 // lvalue in expressions, retain as Node
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