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     */
017    package org.apache.commons.el;
018    
019    import java.lang.reflect.InvocationTargetException;
020    import java.lang.reflect.Method;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.ArrayList;
024    
025    import javax.servlet.jsp.el.ELException;
026    import javax.servlet.jsp.el.FunctionMapper;
027    import javax.servlet.jsp.el.VariableResolver;
028    
029    import org.apache.commons.logging.Log;
030    import 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    
039    public 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    }