/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.internal.gosu.ir.nodes.IRMethodFactory;
import gw.internal.gosu.ir.nodes.IRMethodFromMethodInfo;
import gw.internal.gosu.ir.transform.AbstractElementTransformer;
import gw.internal.gosu.ir.transform.util.NameResolver;
import gw.lang.ir.IRType;
import gw.lang.parser.IExpression;
import gw.lang.parser.IReducedSymbol;
import gw.lang.parser.TypeVarToTypeMap;
import gw.lang.parser.exceptions.ErrantGosuClassException;
import gw.lang.reflect.FunctionType;
import gw.lang.reflect.IAnnotationInfo;
import gw.lang.reflect.IFeatureInfo;
import gw.lang.reflect.IFunctionType;
import gw.lang.reflect.IMethodCallHandler;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.IGenericTypeVariable;
import gw.lang.reflect.gs.IGosuMethodInfo;
import gw.lang.reflect.gs.IGosuProgram;
import gw.lang.reflect.java.JavaTypes;
import gw.util.GosuExceptionUtil;
import gw.util.GosuStringUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*/
public class GosuMethodInfo extends AbstractGenericMethodInfo implements IGosuMethodInfo
{
private IType _returnType;
private IMethodCallHandler _callHandler;
public GosuMethodInfo( IFeatureInfo container, DynamicFunctionSymbol dfs )
{
super( container, dfs );
}
public IType getReturnType()
{
if( _returnType == null )
{
IType rawReturnType = ((FunctionType)getDfs().getType()).getReturnType();
IType ownerType = getOwnersType();
if( ownerType.isParameterizedType() &&
//## Since DFSs are parameterized now, I'm not sure we ever need to get the actual type here (or in the params)
!(getDfs() instanceof ReducedParameterizedDynamicFunctionSymbol) )
{
TypeVarToTypeMap actualParamByVarName = TypeLord.mapTypeByVarName( ownerType, ownerType, true );
for( IGenericTypeVariable tv : getTypeVariables() )
{
if( actualParamByVarName.isEmpty() )
{
actualParamByVarName = new TypeVarToTypeMap();
}
actualParamByVarName.put( tv.getTypeVariableDefinition().getType(), tv.getTypeVariableDefinition().getType() );
}
_returnType = TypeLord.getActualType( rawReturnType, actualParamByVarName, true );
}
else
{
_returnType = rawReturnType;
}
}
if (TypeSystem.isDeleted(_returnType)) {
_returnType =TypeSystem.getErrorType();
}
if( _returnType.isGenericType() && !_returnType.isParameterizedType() )
{
_returnType = TypeLord.getDefaultParameterizedType( _returnType );
}
return _returnType;
}
public IMethodCallHandler getCallHandler()
{
if( _callHandler == null )
{
IGosuClassInternal gsClass = getGosuClass();
if( !gsClass.isValid() )
{
throw new ErrantGosuClassException( gsClass );
}
_callHandler = new GosuMethodCallHandler();
}
return _callHandler;
}
public String getReturnDescription()
{
List<IAnnotationInfo> annotation = getAnnotationsOfType(JavaTypes.getGosuType(gw.lang.Returns.class));
if( annotation.isEmpty() )
{
return "";
}
else
{
String value = (String) annotation.get( 0 ).getFieldValue("value");
return value == null ? "" : value;
}
}
@Override
public IExpression[] getDefaultValueExpressions()
{
List<IExpression> defValues = new ArrayList<IExpression>();
for( IReducedSymbol s : getArgs() )
{
IExpression defValue = s.getDefaultValueExpression();
defValues.add( defValue );
}
return defValues.toArray( new IExpression[defValues.size()] );
}
@Override
public String[] getParameterNames()
{
List<String> names = new ArrayList<String>();
for( IReducedSymbol a : getArgs() )
{
names.add( a.getName() );
}
return names.toArray( new String[names.size()] );
}
public boolean equals( Object o )
{
if( this == o )
{
return true;
}
if( o == null || getClass() != o.getClass() )
{
return false;
}
GosuMethodInfo that = (GosuMethodInfo)o;
return getName().equals(that.getName());
}
public int hashCode()
{
return getName().hashCode();
}
public int compareTo( Object o )
{
return getName().compareTo(((IMethodInfo) o).getName());
}
public boolean isMethodForProperty() {
return getName().startsWith("@");
}
@Override
public IGosuMethodInfo getBackingMethodInfo() {
return this;
}
@Override
public String toString() {
return getName();
}
public static Method getMethod( Class<?> clazz, String strName, Class[] argClasses )
{
if( strName.startsWith( "@" ) )
{
strName = argClasses.length == 1
? "set" + strName.substring( 1 )
: "get" + strName.substring( 1 );
}
Method method = AbstractElementTransformer.getDeclaredMethod( clazz, strName, argClasses );
if( method == null )
{
throw new IllegalStateException( "Could not find method " + strName + "(" + GosuStringUtil.join( ",", argClasses ) + ") on " + clazz );
}
method.setAccessible( true );
return method;
}
protected List<IGosuAnnotation> getGosuAnnotations()
{
ReducedDynamicFunctionSymbol dfs = getDfs();
if( dfs instanceof ReducedDelegateFunctionSymbol )
{
IMethodInfo miTarget = ((ReducedDelegateFunctionSymbol)dfs).getTargetMethodInfo();
if( miTarget != this && miTarget instanceof AbstractGenericMethodInfo )
{
if( getOwnersType().isCompiled() ) {
// Ensure the delegate's owner is fully compiled, otherwise the annotations won't be fully formed (have NewExpressions, see PL-21981)
miTarget.getOwnersType().isValid();
}
return ((AbstractGenericMethodInfo)miTarget).getGosuAnnotations();
}
}
return super.getGosuAnnotations();
}
//----------------------------------------------------------------------------
// -- private methods --
private class GosuMethodCallHandler implements IMethodCallHandler
{
public Object handleCall( Object gsClassInstance, Object... args )
{
ReducedDynamicFunctionSymbol dfs = getDfs();
try
{
boolean isEnhancement = AbstractElementTransformer.requiresImplicitEnhancementArg( dfs );
IGosuClassInternal dfsClass = dfs.getGosuClass();
// If this is an enhancement method or requires method type variable arguments
// do the dirty work of extracting the appropriate argumetns
if( isEnhancement || dfs.hasTypeVariables() || dfs.getGosuClass() instanceof IGosuProgram )
{
List<Object> argList = new ArrayList<Object>();
//Handle enhancement args
if( isEnhancement )
{
argList.add( gsClassInstance );
if( !dfs.isStatic() )
{
if( dfsClass.isParameterizedType() )
{
argList.addAll( Arrays.asList( dfsClass.getTypeParameters() ) );
}
else
{
for( int i = 0; i < dfsClass.getGenericTypeVariables().length; i++ )
{
IGenericTypeVariable tv = dfsClass.getGenericTypeVariables()[i];
argList.add( tv.getBoundingType() );
}
}
}
}
//handle function args
for( IGenericTypeVariable typeVar : dfs.getType().getGenericTypeVariables() )
{
argList.add( typeVar.getBoundingType() );
}
// If it's an instance of a Gosu program, for now pass through null for the external symbols argument
// if ( dfs.getGosuClass() instanceof IGosuProgram ) {
// argList.add( null );
// }
if ( args != null ) {
argList.addAll( Arrays.asList( args ) );
}
args = argList.toArray();
}
Class clazz = dfsClass.getBackingClass();
IRMethodFromMethodInfo irMethod = IRMethodFactory.createIRMethod(GosuMethodInfo.this, (IFunctionType) dfs.getType());
List<IRType> allParameterTypes = irMethod.getAllParameterTypes();
Class[] paramClasses = new Class[allParameterTypes.size()];
for (int i = 0; i < allParameterTypes.size(); i++) {
paramClasses[i] = allParameterTypes.get(i).getJavaClass();
}
Method method = getMethod( clazz, NameResolver.getFunctionName( dfs ), paramClasses );
return method.invoke( gsClassInstance, args );
}
catch( IllegalAccessException e )
{
throw GosuExceptionUtil.forceThrow( e );
}
catch( InvocationTargetException e )
{
throw GosuExceptionUtil.forceThrow( e.getTargetException() );
}
}
}
}