/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.config.CommonServices;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import gw.lang.reflect.IMethodCallHandler;
import gw.lang.reflect.Modifier;
import gw.lang.reflect.TypeSystem;
import gw.lang.parser.EvaluationException;
import gw.util.GosuExceptionUtil;
import gw.util.GosuClassUtil;
/**
* Handles Gosu->Java method call adaptation
*/
public class MethodCallAdapter implements IMethodCallHandler
{
private Method _method = null;
private Class[] _argTypes = null;
private boolean _external;
public MethodCallAdapter( Method method )
{
_method = method;
_argTypes = method.getParameterTypes(); // Cache this so we don't have to create a copy every time
_method.setAccessible( true );
_external = CommonServices.getEntityAccess().isExternal( _method.getDeclaringClass() );
}
public Object handleCall( Object ctx, Object... argValues )
{
if( argValues == null ? _argTypes.length != 0 : _argTypes.length != argValues.length )
{
throw new EvaluationException( "Wrong number of arguments for method " + _method.getName() + " in class " +
_method.getDeclaringClass().getName() );
}
if( !Modifier.isStatic( _method.getModifiers() ) )
{
if( ctx == null )
{
throw new EvaluationException( "Tried to invoke method from null reference: " + _method.getDeclaringClass().getName() + "#" + _method.getName() );
}
if( !_method.getDeclaringClass().isAssignableFrom( ctx.getClass() ) )
{
throw new EvaluationException( "Tried to invoke method from a context not compatible with method's declaring class.\nContext: " + ctx.getClass().getName() + "\nMethod: " + _method.getDeclaringClass().getName() + "#" + _method.getName() );
}
}
ClassLoader previousClassLoader = null;
boolean bMethodOnThread = Thread.class.isAssignableFrom( _method.getDeclaringClass() );
if( !bMethodOnThread )
{
previousClassLoader = Thread.currentThread().getContextClassLoader();
if(TypeSystem.getCurrentModule() != null) {
Thread.currentThread().setContextClassLoader( TypeSystem.getGosuClassLoader().getActualLoader() ); //_method.getDeclaringClass().getClassLoader() );
}
}
try
{
if( _external && argValues != null && argValues.length > 0 )
{
argValues = CommonServices.getEntityAccess().convertToExternalIfNecessary(
argValues, _argTypes, _method.getDeclaringClass() );
}
Object result = _method.invoke( ctx, argValues );
if( _external )
{
return CommonServices.getEntityAccess().convertToInternalIfNecessary( result, _method.getDeclaringClass() );
}
else
{
return result;
}
}
catch( InvocationTargetException ex )
{
throw GosuExceptionUtil.forceThrow( ex.getCause() );
}
catch( IllegalArgumentException ie )
{
GosuExceptionUtil.throwArgMismatchException( ie, "method \"" + GosuClassUtil.getShortClassName( _method.getDeclaringClass() ) + "#" + _method.getName() + "\"", _method.getParameterTypes(), argValues );
return null;
}
catch( Throwable t )
{
throw makeMethodCallEvaluationException( _method, t );
}
finally
{
if( !bMethodOnThread )
{
Thread.currentThread().setContextClassLoader( previousClassLoader );
}
}
}
public Method getMethod()
{
return _method;
}
private RuntimeException makeMethodCallEvaluationException( Method method, Throwable t )
{
String params = "";
for( int i = 0; i < method.getParameterTypes().length; i++ )
{
Class aClass = method.getParameterTypes()[i];
if( i != 0 )
{
params += ", ";
}
params += aClass.getName();
}
return new RuntimeException(
"could not invoke " + method.getName() +
"(" + params + ")" +
" on class " + _method.getDeclaringClass().getName(), t );
}
}