/* * Copyright 2013 Guidewire Software, Inc. */ package gw.lang.parser.coercers; import gw.config.CommonServices; import gw.lang.GosuShop; import gw.lang.function.IBlock; import gw.lang.parser.ICoercer; import gw.lang.parser.IResolvingCoercer; import gw.lang.reflect.IFunctionType; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IParameterInfo; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeInfo; import gw.lang.reflect.ITypeVariableType; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.gs.IGosuEnhancement; import gw.lang.reflect.gs.IGosuMethodInfo; import gw.lang.reflect.gs.IGosuObject; import gw.lang.reflect.java.IJavaMethodInfo; import gw.lang.reflect.java.IJavaType; import gw.lang.reflect.java.JavaTypes; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class FunctionToInterfaceCoercer extends BaseCoercer implements IResolvingCoercer { private static FunctionToInterfaceCoercer _instance = new FunctionToInterfaceCoercer(); public static FunctionToInterfaceCoercer instance() { return _instance; } private FunctionToInterfaceCoercer() { } public Object coerceValue( IType typeToCoerceTo, Object value ) { if( value instanceof IBlock ) { Class proxyClass = GosuShop.getBlockToInterfaceConversionClass( typeToCoerceTo ); IBlock blk = (IBlock)value; IType methodReturnType = getSingleMethod( typeToCoerceTo ).getReturnType(); try { Field field = blk.getClass().getField( "_returnType" ); ICoercer coercer = getCoercer( (IType)field.get( blk ), methodReturnType ); return proxyClass.getConstructor(IBlock.class, ICoercer.class, IType.class).newInstance(blk, coercer, methodReturnType ); } catch (Exception e) { throw new RuntimeException(e); } } else { throw new IllegalStateException(); } } private ICoercer getCoercer( IType type, IType returnType ) { if( !returnType.isAssignableFrom( type ) ) { final ICoercer coercer = CommonServices.getCoercionManager().findCoercer( returnType, type, true ); if( coercer == null ) { if( JavaTypes.pVOID().equals(returnType) ) { return IdentityCoercer.instance(); } throw new IllegalStateException( "Unable to coerce return value " + type + " of block to " + returnType ); } return coercer; } return null; } public static IFunctionType getRepresentativeFunctionType( IType interfaceType ) { IMethodInfo javaMethodInfo = getSingleMethod( interfaceType ); if( javaMethodInfo != null ) { return GosuShop.createFunctionType( javaMethodInfo ); } else { return null; } } public static IMethodInfo getSingleMethod( IType interfaceType ) { if( interfaceType.isInterface() && (interfaceType instanceof IJavaType || interfaceType instanceof IGosuClass) ) { List<IMethodInfo> list = new ArrayList<IMethodInfo>( interfaceType.getTypeInfo().getMethods() ); //extract all object methods since they are guaranteed to be implemented ITypeInfo objTypeInfo = JavaTypes.OBJECT().getTypeInfo(); for( Iterator<? extends IMethodInfo> it = list.iterator(); it.hasNext(); ) { IMethodInfo methodInfo = it.next(); IParameterInfo[] parameterInfos = methodInfo.getParameters(); IType[] paramTypes = new IType[parameterInfos.length]; for( int i = 0; i < parameterInfos.length; i++ ) { paramTypes[i] = parameterInfos[i].getFeatureType(); } if( objTypeInfo.getMethod( methodInfo.getDisplayName(), paramTypes ) != null || methodInfo.getOwnersType() instanceof IGosuEnhancement ) { it.remove(); } else if( methodInfo.getOwnersType().getName().contains( IGosuObject.class.getName() ) ) { it.remove(); } } if( list.size() == 1 ) { IMethodInfo mi = list.get( 0 ); if( mi instanceof IJavaMethodInfo || mi instanceof IGosuMethodInfo ) { return mi; } } } return null; } public boolean isExplicitCoercion() { return false; } public boolean handlesNull() { return false; } public int getPriority( IType to, IType from ) { return 0; } public IType resolveType( IType target, IType source ) { IFunctionType sourceFun = (IFunctionType)source; IType returnType = sourceFun.getReturnType(); IType methodReturnType = extractReturnTypeFromInterface( target ); if( methodReturnType instanceof ITypeVariableType ) { IGenericTypeVariable[] typeVariables = target.getGenericTypeVariables(); IType[] parameterizationTypes = new IType[typeVariables.length]; for( int i = 0; i < typeVariables.length; i++ ) { IGenericTypeVariable typeVariable = typeVariables[i]; if( typeVariable.getName().equals( methodReturnType.getName() ) ) { parameterizationTypes[i] = returnType; } else { parameterizationTypes[i] = target.getTypeParameters()[i]; } } return target.getParameterizedType( parameterizationTypes ); } else { return target; } } private IType extractReturnTypeFromInterface( IType target ) { for( IMethodInfo methodInfo : target.getTypeInfo().getMethods() ) { if( methodInfo.getOwnersType().equals( target ) ) { return methodInfo.getReturnType(); } } return null; } }