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 }