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

Classes in this File Line Coverage Branch Coverage Complexity
ELEvaluator
52% 
89% 
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