001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.el;
018
019import java.lang.reflect.InvocationTargetException;
020import java.lang.reflect.Method;
021import java.util.Iterator;
022import java.util.List;
023import java.util.ArrayList;
024
025import javax.servlet.jsp.el.ELException;
026import javax.servlet.jsp.el.FunctionMapper;
027import javax.servlet.jsp.el.VariableResolver;
028
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031
032/**
033 *
034 * <p>Represents a function call.</p>
035 * 
036 * @author Shawn Bayern (in the style of Nathan's other classes)
037 **/
038
039public class FunctionInvocation
040  extends Expression
041{
042    //-------------------------------------
043    // Constants
044    //-------------------------------------
045    private static Log log = LogFactory.getLog(FunctionInvocation.class);
046    
047  //-------------------------------------
048  // Properties
049  //-------------------------------------
050  // property index
051
052  private String functionName;
053  private List argumentList;
054  public String getFunctionName() { return functionName; }
055  public void setFunctionName(String f) { functionName = f; }
056  public List getArgumentList() { return argumentList; }
057  public void setArgumentList(List l) { argumentList = l; }
058
059  //-------------------------------------
060  /**
061   * Constructor
062   **/
063  public FunctionInvocation (String functionName, List argumentList)
064  {
065    this.functionName = functionName;
066    this.argumentList = argumentList;
067  }
068
069  //-------------------------------------
070  // Expression methods
071  //-------------------------------------
072  /**
073   * Returns the expression in the expression language syntax
074   **/
075  public String getExpressionString ()
076  {
077    StringBuffer b = new StringBuffer();
078    b.append(functionName);
079    b.append("(");
080    Iterator i = argumentList.iterator();
081    while (i.hasNext()) {
082      b.append(((Expression) i.next()).getExpressionString());
083      if (i.hasNext())
084        b.append(", ");
085    }
086    b.append(")");
087    return b.toString();
088  }
089
090
091  //-------------------------------------
092  /**
093   *
094   * Evaluates by looking up the name in the VariableResolver
095   **/
096  public Object evaluate (VariableResolver pResolver,
097                          FunctionMapper functions)
098    throws ELException
099  {
100
101    Method target = resolveFunction(functions);
102    if (target == null) {
103        if (log.isErrorEnabled()) {
104            String message = MessageUtil.getMessageWithArgs(
105                Constants.UNKNOWN_FUNCTION, functionName);
106            log.error(message);
107            throw new ELException(message);
108        }
109    }      
110
111    // ensure that the number of arguments matches the number of parameters
112    Class[] params = target.getParameterTypes();
113    if (params.length != argumentList.size()) {
114        if (log.isErrorEnabled()) {
115            String message = MessageUtil.getMessageWithArgs(
116                Constants.INAPPROPRIATE_FUNCTION_ARG_COUNT,
117                functionName, new Integer(params.length),
118                new Integer(argumentList.size()));
119            log.error(message);
120            throw new ELException(message);
121        }      
122    }
123
124    // now, walk through each parameter, evaluating and casting its argument
125    Object[] arguments = new Object[argumentList.size()];
126    for (int i = 0; i < params.length; i++) {
127      // evaluate
128      arguments[i] = ((Expression) argumentList.get(i)).evaluate(pResolver,
129                                                                 functions);
130      // coerce
131      arguments[i] = Coercions.coerce(arguments[i], params[i]);
132    }
133
134    // finally, invoke the target method, which we know to be static
135    try {
136      return (target.invoke(null, arguments));
137    } catch (InvocationTargetException ex) {
138        if (log.isErrorEnabled()) {
139            String message = MessageUtil.getMessageWithArgs(
140                Constants.FUNCTION_INVOCATION_ERROR,
141                functionName);
142            Throwable t = ex.getTargetException();
143            log.error(message, t);
144            throw new ELException(message, t);
145        }      
146      return null;
147    } catch (Throwable t) {
148        if (log.isErrorEnabled()) {
149            String message = MessageUtil.getMessageWithArgs(
150                Constants.FUNCTION_INVOCATION_ERROR,
151                functionName);            
152            log.error(message, t);
153            throw new ELException(message, t); 
154        }      
155      return null;
156    }
157  }
158
159  /**
160   * Returns the <code>Method</code> which is mapped to the function
161   * name used by this <code>FunctionInvocation</code>.
162   * @param functions The function mappings in use for this evaluation
163   * @return the <code>Method</code> to execute 
164   * @throws ELException
165   */
166  protected Method resolveFunction(FunctionMapper functions) throws ELException {
167      // if the Map is null, then the function is invalid 
168      if (functions == null) { 
169          return null;
170      }                    
171
172      // normalize function name
173      String prefix = null; 
174      String localName = null; 
175      int index = functionName.indexOf( ':' );
176      if (index == -1) {
177        prefix = "";
178        localName = functionName;
179      } else {
180        prefix = functionName.substring( 0, index );
181        localName = functionName.substring( index + 1 );
182      }       
183  
184      // ensure that the function's name is mapped
185      Method target = (Method) functions.resolveFunction(prefix, localName);
186   
187       return target; 
188   }
189
190   public Expression bindFunctions(final FunctionMapper functions)
191           throws ELException {
192       final List argList = new ArrayList(argumentList.size());
193       for (Iterator argIter = argumentList.iterator(); argIter.hasNext();) {
194           Expression arg = (Expression) argIter.next();
195           argList.add(arg.bindFunctions(functions));
196       }
197       return new BoundFunctionInvocation(
198               resolveFunction(functions),
199               functionName,
200               argList);
201   }
202
203  //-------------------------------------
204}