View Javadoc

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