Source code: org/apache/myfaces/el/MethodBindingImpl.java
1 /*
2 * Copyright 2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.apache.myfaces.el;
17
18 import org.apache.myfaces.el.ValueBindingImpl.NotVariableReferenceException;
19
20 import org.apache.commons.beanutils.MethodUtils;
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23
24 import javax.faces.application.Application;
25 import javax.faces.component.StateHolder;
26 import javax.faces.context.FacesContext;
27 import javax.faces.el.*;
28 import javax.faces.event.AbortProcessingException;
29 import javax.faces.validator.ValidatorException;
30 import javax.servlet.jsp.el.ELException;
31 import java.lang.reflect.InvocationTargetException;
32 import java.lang.reflect.Method;
33
34
35 /**
36 * @author Anton Koinov (latest modification by $Author: oros $)
37 * @version $Revision: 278654 $ $Date: 2005-09-04 20:32:35 -0400 (Sun, 04 Sep 2005) $
38 */
39 public class MethodBindingImpl extends MethodBinding
40 implements StateHolder
41 {
42 static final Log log = LogFactory.getLog(MethodBindingImpl.class);
43
44 //~ Instance fields -------------------------------------------------------
45
46 ValueBindingImpl _valueBinding;
47 Class[] _argClasses;
48
49 //~ Constructors ----------------------------------------------------------
50
51 public MethodBindingImpl(Application application, String reference,
52 Class[] argClasses)
53 {
54 // Note: using ValueBindingImpl, istead of creating a common subclass,
55 // to share single Expression cache
56 // Note: we can trim() reference, since string-binding mixed
57 // expressions are not allowed for MethodBindings
58 _valueBinding = new ValueBindingImpl(application, reference.trim());
59 _argClasses = argClasses;
60 }
61
62 //~ Methods ---------------------------------------------------------------
63
64 public String getExpressionString()
65 {
66 return _valueBinding._expressionString;
67 }
68
69 public Class getType(FacesContext facesContext)
70 {
71 if (facesContext == null) {
72 throw new NullPointerException("facesContext");
73 }
74 try
75 {
76 Object[] baseAndProperty = resolveToBaseAndProperty(facesContext);
77 Object base = baseAndProperty[0];
78 Object property = baseAndProperty[1];
79
80 Class returnType = base.getClass().getMethod(property.toString(), _argClasses).getReturnType();
81
82 if (returnType.getName().equals("void")) {
83 // the spec document says: "if type is void return null"
84 // but the RI returns Void.class, so let's follow the RI
85 return Void.class;
86 }
87 return returnType;
88 }
89 catch (ReferenceSyntaxException e)
90 {
91 throw e;
92 }
93 catch (IndexOutOfBoundsException e)
94 {
95 // ArrayIndexOutOfBoundsException also here
96 throw new PropertyNotFoundException("Expression: "
97 + getExpressionString(), e);
98 }
99 catch (Exception e)
100 {
101 throw new EvaluationException("Cannot get type for expression "
102 + getExpressionString(), e);
103 }
104 }
105
106 public Object invoke(FacesContext facesContext, Object[] args)
107 throws EvaluationException, MethodNotFoundException
108 {
109 if (facesContext == null) {
110 throw new NullPointerException("facesContext");
111 }
112 try
113 {
114 Object[] baseAndProperty = resolveToBaseAndProperty(facesContext);
115 Object base = baseAndProperty[0];
116 Object property = baseAndProperty[1];
117
118 Method m = base.getClass().getMethod(property.toString(), _argClasses);
119
120 // Check if the concrete class of this method is accessible and if not
121 // search for a public interface that declares this method
122 m = MethodUtils.getAccessibleMethod(m);
123 if (m == null)
124 {
125 throw new MethodNotFoundException(
126 getExpressionString() + " (not accessible!)");
127 }
128
129 return m.invoke(base, args);
130 }
131 catch (ReferenceSyntaxException e)
132 {
133 throw e;
134 }
135 catch (IndexOutOfBoundsException e)
136 {
137 // ArrayIndexOutOfBoundsException also here
138 throw new PropertyNotFoundException("Expression: "
139 + getExpressionString(), e);
140 }
141 catch (InvocationTargetException e)
142 {
143 Throwable cause = e.getCause();
144 if (cause != null)
145 {
146 if (cause instanceof ValidatorException ||
147 cause instanceof AbortProcessingException)
148 {
149 throw new EvaluationException(cause);
150 }
151 else
152 {
153 throw new EvaluationException("Exception while invoking expression "
154 + getExpressionString(), cause);
155 }
156 }
157 else
158 {
159 throw new EvaluationException("Exception while invoking expression "
160 + getExpressionString(), e);
161 }
162 }
163 catch (Exception e)
164 {
165 throw new EvaluationException("Exception while invoking expression "
166 + getExpressionString(), e);
167 }
168 }
169
170 protected Object[] resolveToBaseAndProperty(FacesContext facesContext)
171 throws ELException
172 {
173 if (facesContext == null)
174 {
175 throw new NullPointerException("facesContext");
176 }
177
178 try
179 {
180 Object base = _valueBinding.resolveToBaseAndProperty(facesContext);
181
182 if (!(base instanceof Object[]))
183 {
184 String errorMessage = "Expression not a valid method binding: "
185 + getExpressionString();
186 throw new ReferenceSyntaxException(errorMessage);
187 }
188
189 return (Object[]) base;
190 }
191 catch (NotVariableReferenceException e)
192 {
193 throw new ReferenceSyntaxException("Expression: "
194 + getExpressionString(), e);
195 }
196 }
197
198 public String toString()
199 {
200 return _valueBinding.toString();
201 }
202
203 //~ StateHolder implementation --------------------------------------------
204
205 private boolean _transient = false;
206
207 /**
208 * Empty constructor, so that new instances can be created when restoring
209 * state.
210 */
211 public MethodBindingImpl()
212 {
213 _valueBinding = null;
214 _argClasses = null;
215 }
216
217 public Object saveState(FacesContext facescontext)
218 {
219 return new Object[] { _valueBinding.saveState(facescontext),
220 _argClasses};
221 }
222
223 public void restoreState(FacesContext facescontext, Object obj)
224 {
225 Object[] ar = (Object[]) obj;
226 _valueBinding = new ValueBindingImpl();
227 _valueBinding.restoreState(facescontext, ar[0]);
228 _argClasses = (Class[]) ar[1];
229 }
230
231 public boolean isTransient()
232 {
233 return _transient;
234 }
235
236 public void setTransient(boolean flag)
237 {
238 _transient = flag;
239 }
240
241 }