/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.myfaces.el;
import org.apache.myfaces.el.ValueBindingImpl.NotVariableReferenceException;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.faces.application.Application;
import javax.faces.component.StateHolder;
import javax.faces.context.FacesContext;
import javax.faces.el.*;
import javax.faces.event.AbortProcessingException;
import javax.faces.validator.ValidatorException;
import javax.servlet.jsp.el.ELException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author Anton Koinov (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public class MethodBindingImpl extends MethodBinding
implements StateHolder
{
static final Log log = LogFactory.getLog(MethodBindingImpl.class);
//~ Instance fields -------------------------------------------------------
ValueBindingImpl _valueBinding;
Class[] _argClasses;
//~ Constructors ----------------------------------------------------------
public MethodBindingImpl(Application application, String reference,
Class[] argClasses)
{
// Note: using ValueBindingImpl, istead of creating a common subclass,
// to share single Expression cache
// Note: we can trim() reference, since string-binding mixed
// expressions are not allowed for MethodBindings
_valueBinding = new ValueBindingImpl(application, reference.trim());
_argClasses = argClasses;
}
//~ Methods ---------------------------------------------------------------
public String getExpressionString()
{
return _valueBinding._expressionString;
}
public Class getType(FacesContext facesContext)
{
if (facesContext == null) {
throw new NullPointerException("facesContext");
}
try
{
Object[] baseAndProperty = resolveToBaseAndProperty(facesContext);
Object base = baseAndProperty[0];
Object property = baseAndProperty[1];
Class returnType = base.getClass().getMethod(property.toString(), _argClasses).getReturnType();
if (returnType.getName().equals("void")) {
// the spec document says: "if type is void return null"
// but the RI returns Void.class, so let's follow the RI
return Void.class;
}
return returnType;
}
catch (ReferenceSyntaxException e)
{
throw e;
}
catch (IndexOutOfBoundsException e)
{
// ArrayIndexOutOfBoundsException also here
throw new PropertyNotFoundException("Expression: "
+ getExpressionString(), e);
}
catch (Exception e)
{
throw new EvaluationException("Cannot get type for expression "
+ getExpressionString(), e);
}
}
public Object invoke(FacesContext facesContext, Object[] args)
throws EvaluationException, MethodNotFoundException
{
if (facesContext == null) {
throw new NullPointerException("facesContext");
}
try
{
Object[] baseAndProperty = resolveToBaseAndProperty(facesContext);
Object base = baseAndProperty[0];
Object property = baseAndProperty[1];
Method m = base.getClass().getMethod(property.toString(), _argClasses);
// Check if the concrete class of this method is accessible and if not
// search for a public interface that declares this method
m = MethodUtils.getAccessibleMethod(m);
if (m == null)
{
throw new MethodNotFoundException(
getExpressionString() + " (not accessible!)");
}
return m.invoke(base, args);
}
catch (ReferenceSyntaxException e)
{
throw e;
}
catch (IndexOutOfBoundsException e)
{
// ArrayIndexOutOfBoundsException also here
throw new PropertyNotFoundException("Expression: "
+ getExpressionString(), e);
}
catch (InvocationTargetException e)
{
Throwable cause = e.getCause();
if (cause != null)
{
if (cause instanceof ValidatorException ||
cause instanceof AbortProcessingException)
{
throw new EvaluationException(cause);
}
else
{
throw new EvaluationException("Exception while invoking expression "
+ getExpressionString(), cause);
}
}
else
{
throw new EvaluationException("Exception while invoking expression "
+ getExpressionString(), e);
}
}
catch (Exception e)
{
throw new EvaluationException("Exception while invoking expression "
+ getExpressionString(), e);
}
}
protected Object[] resolveToBaseAndProperty(FacesContext facesContext)
throws ELException
{
if (facesContext == null)
{
throw new NullPointerException("facesContext");
}
try
{
Object base = _valueBinding.resolveToBaseAndProperty(facesContext);
if (!(base instanceof Object[]))
{
String errorMessage = "Expression not a valid method binding: "
+ getExpressionString();
throw new ReferenceSyntaxException(errorMessage);
}
return (Object[]) base;
}
catch (NotVariableReferenceException e)
{
throw new ReferenceSyntaxException("Expression: "
+ getExpressionString(), e);
}
}
public String toString()
{
return _valueBinding.toString();
}
//~ StateHolder implementation --------------------------------------------
private boolean _transient = false;
/**
* Empty constructor, so that new instances can be created when restoring
* state.
*/
public MethodBindingImpl()
{
_valueBinding = null;
_argClasses = null;
}
public Object saveState(FacesContext facescontext)
{
return new Object[] { _valueBinding.saveState(facescontext),
_argClasses};
}
public void restoreState(FacesContext facescontext, Object obj)
{
Object[] ar = (Object[]) obj;
_valueBinding = new ValueBindingImpl();
_valueBinding.restoreState(facescontext, ar[0]);
_argClasses = (Class[]) ar[1];
}
public boolean isTransient()
{
return _transient;
}
public void setTransient(boolean flag)
{
_transient = flag;
}
}