/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.runtime; import gw.config.CommonServices; import gw.internal.gosu.ir.transform.AbstractElementTransformer; import gw.internal.gosu.parser.TypeLord; import gw.lang.reflect.IExpando; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IPlaceholder; import gw.lang.reflect.IPropertyInfo; import gw.lang.reflect.IRelativeTypeInfo; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeInfo; import gw.lang.reflect.ReflectUtil; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.java.IJavaType; import gw.lang.reflect.java.JavaTypes; import gw.util.GosuExceptionUtil; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class GosuRuntimeMethods { public static Object getProperty( Object root, IType type, String propertyName ) { if( root != null && IExpando.class.isAssignableFrom( root.getClass() ) ) { return ((IExpando)root).getFieldValue( propertyName ); } if( isDynamic( type ) ) { type = TypeSystem.getFromObject( root ); } Object ret = invokePropertyGetter( "$getProperty", root, type, propertyName ); if( ret != IPlaceholder.UNHANDLED ) { return ret; } IPropertyInfo propertyInfo = getPropertyInfo( root, type, propertyName ); if( propertyInfo == null ) { ret = invokePropertyGetter( "$getMissingProperty", root, type, propertyName ); if( ret == IPlaceholder.UNHANDLED ) { throw new IllegalArgumentException( "No property named " + propertyName + " found on type " + type.getName() ); } return ret; } return propertyInfo.getAccessor().getValue( root ); } private static boolean isDynamic( IType type ) { return (type instanceof IPlaceholder && ((IPlaceholder)type).isPlaceholder()) || (type instanceof IGosuClass && ((IGosuClass)type).isStructure()); } private static Object invokePropertyGetter( String dispatchName, Object root, IType type, String propertyName ) { ITypeInfo typeInfo = type.getTypeInfo(); IMethodInfo method; if( typeInfo instanceof IRelativeTypeInfo ) { method = ((IRelativeTypeInfo) typeInfo).getMethod( type, dispatchName, JavaTypes.STRING() ); } else { method = typeInfo.getMethod( dispatchName, JavaTypes.STRING() ); } return method == null ? IPlaceholder.UNHANDLED : method.getCallHandler().handleCall( root, propertyName ); } public static Object getPropertyDynamically(Object rootObject, String propertyName) { if (rootObject == null) { throw new NullPointerException(); } return getProperty(rootObject, TypeSystem.getFromObject(rootObject), propertyName); } public static void setProperty( Object root, IType type, String propertyName, Object value ) { if( root != null && IExpando.class.isAssignableFrom( root.getClass() ) ) { ((IExpando)root).setFieldValue( propertyName, value ); return; } if( isDynamic( type ) ) { type = TypeSystem.getFromObject( root ); } Object ret = invokePropertySetter( "$setProperty", root, type, propertyName ); if( ret != IPlaceholder.UNHANDLED ) { return; } IPropertyInfo propertyInfo = getPropertyInfo( root, type, propertyName ); if( propertyInfo == null ) { ret = invokePropertySetter( "$setMissingProperty", root, type, propertyName ); if( ret == IPlaceholder.UNHANDLED ) { throw new IllegalArgumentException( "No property named " + propertyName + " found on type " + type.getName() ); } return; } propertyInfo.getAccessor().setValue( root, value ); } private static Object invokePropertySetter( String dispatchName, Object root, IType type, String propertyName, Object... args ) { ITypeInfo typeInfo = type.getTypeInfo(); IMethodInfo method; if( typeInfo instanceof IRelativeTypeInfo ) { method = ((IRelativeTypeInfo) typeInfo).getMethod( type, dispatchName, JavaTypes.STRING(), JavaTypes.OBJECT() ); } else { method = typeInfo.getMethod( dispatchName, JavaTypes.STRING(), JavaTypes.OBJECT() ); } return method == null ? IPlaceholder.UNHANDLED : method.getCallHandler().handleCall( root, propertyName, args ); } public static void setPropertyDynamically(Object rootObject, String propertyName, Object value) { if (rootObject == null) { throw new NullPointerException(); } setProperty(rootObject, TypeSystem.getFromObject(rootObject), propertyName, value); } private static IPropertyInfo getPropertyInfo( Object rootObject, IType type, String propertyName ) { IPropertyInfo propertyInfo = ReflectUtil.findProperty( type, propertyName ); if( propertyInfo == null ) { propertyInfo = ReflectUtil.findProperty( TypeSystem.getFromObject( rootObject ), propertyName ); if( propertyInfo == null ) { return null; } } return propertyInfo; } public static Object initMultiArray( IType componentType, Object instance, int iDimension, int[] sizes ) { if( sizes.length <= iDimension-1 ) { return instance; } int iLength = componentType.getArrayLength( instance ); componentType = componentType.getComponentType(); for( int i = 0; i < iLength; i++ ) { Object component = componentType.makeArrayInstance( sizes[iDimension-1] ); initMultiArray( componentType, component, iDimension + 1, sizes ); componentType.setArrayComponent( instance, i, component ); } return instance; } public static IType getType( Object obj ) { return TypeSystem.get( obj.getClass() ); } public static Object invokeMethod( Class c, String methodName, Class[] argTypes, Object root, Object[] args ) { Method declaredMethod = AbstractElementTransformer.getDeclaredMethod( c, methodName, argTypes ); try { return declaredMethod.invoke( root, args ); } catch( IllegalAccessException e ) { throw GosuExceptionUtil.forceThrow( e ); } catch( InvocationTargetException e ) { throw GosuExceptionUtil.forceThrow( e.getTargetException() ); } } public static Object invokeMethodInfo( IType type, String methodName, IType[] parameterTypes, Object root, Object[] args ) { if( root instanceof IExpando ) { return ((IExpando)root).invoke( methodName, args ); } boolean bDynamicType = isDynamic( type ); if( bDynamicType ) { type = TypeSystem.getFromObject( root ); } Object ret = invokeMethodInvoker( "$invokeMethod", root, type, methodName, args ); if( ret != IPlaceholder.UNHANDLED ) { return ret; } ITypeInfo typeInfo = type.getTypeInfo(); IMethodInfo method; if( bDynamicType ) { IType[] runtimeTypes = ReflectUtil.extractRuntimeTypes( args ); method = ReflectUtil.findCallableMethod( methodName, runtimeTypes, type ); } else { parameterTypes = replaceDynamicTypesWithRuntimeTypes( parameterTypes, args ); if( typeInfo instanceof IRelativeTypeInfo ) { method = ((IRelativeTypeInfo)typeInfo).getMethod( type, methodName, parameterTypes ); } else { method = typeInfo.getMethod( methodName, parameterTypes ); } } if( method == null ) { ret = invokeMethodInvoker( "$invokeMissingMethod", root, type, methodName, args ); if( ret == IPlaceholder.UNHANDLED ) { throw new IllegalStateException( "Could not find method for " + methodName + " on " + type.getName() + " with specified param types" ); } return ret; } if( bDynamicType ) { args = ReflectUtil.coerceArgsIfNecessary( method.getParameters(), args ); } return method.getCallHandler().handleCall( root, args ); } private static IType[] replaceDynamicTypesWithRuntimeTypes( IType[] parameterTypes, Object[] args ) { if( parameterTypes == null ) { return null; } IType[] ret = null; for( int i = 0; i < parameterTypes.length; i++ ) { IType type = parameterTypes[i]; if( type instanceof IPlaceholder && ((IPlaceholder)type).isPlaceholder() ) { if( ret == null ) { ret = new IType[parameterTypes.length]; System.arraycopy( parameterTypes, 0, ret, 0, ret.length ); } ret[i] = args[i] == null ? ret[i] : TypeSystem.getFromObject( args[i] ); } } return ret == null ? parameterTypes : ret; } private static Object invokeMethodInvoker( String dispatchName, Object root, IType type, String methodName, Object... args ) { ITypeInfo typeInfo = type.getTypeInfo(); IMethodInfo method; if( typeInfo instanceof IRelativeTypeInfo ) { method = ((IRelativeTypeInfo)typeInfo).getMethod( type, dispatchName, JavaTypes.STRING(), JavaTypes.OBJECT().getArrayType() ); } else { method = typeInfo.getMethod( dispatchName, JavaTypes.STRING(), JavaTypes.OBJECT().getArrayType() ); } return method == null ? IPlaceholder.UNHANDLED : method.getCallHandler().handleCall( root, methodName, args ); } public static Class lookUpClass( String className ) { if (className.startsWith("L") && className.endsWith(";")) { className = className.substring(1, className.length() -1 ); } className = className.replaceAll("/", "."); try { return Class.forName(className, false, GosuRuntimeMethods.class.getClassLoader()); } catch( ClassNotFoundException e ) { throw GosuExceptionUtil.forceThrow( e ); } } public static void invokeLockMethod( Object o ) { if( o != null ) { IMethodInfo iMethodInfo = TypeSystem.getFromObject( o ).getTypeInfo().getMethod( "lock" ); if( iMethodInfo != null ) { iMethodInfo.getCallHandler().handleCall( o ); } } } public static IType typeof( Object o ) { IType type = TypeSystem.getFromObject( o ); if( type instanceof IJavaType && type.isGenericType() ) { // Never return a generic type resulting from Java's generic type erasure. // Instead return the "erased" or default type. type = TypeLord.getDefaultParameterizedType( type ); } return type; } public static boolean logicalNot( Object o ) { if( o instanceof Boolean ) { return !((Boolean)o).booleanValue(); } return !CommonServices.getCoercionManager().makePrimitiveBooleanFrom( o ); } public static void invokeUnlockOrDisposeOrCloseMethod( Object o ) { if( o != null ) { ITypeInfo ti = TypeSystem.getFromObject( o ).getTypeInfo(); IMethodInfo mi = ti.getMethod( "unlock" ); if( mi != null ) { mi.getCallHandler().handleCall( o ); } else { mi = ti.getMethod( "dispose" ); if( mi != null ) { mi.getCallHandler().handleCall( o ); } else { mi = ti.getMethod( "close" ); if( mi != null ) { mi.getCallHandler().handleCall( o ); } else { } } } } } public static void print( Object obj ) { System.out.println( toString( obj ) ); } public static String toString( Object obj ) { if ( obj == null ) { return "null"; } if ( obj instanceof Byte ) { int value = (Byte) obj; if ( value < 0 ) { value = 256 + value; } return "0x" + Integer.toHexString( value ); } IType type = TypeSystem.getFromObject( obj ); if ( type.isArray() ) { StringBuilder sb = new StringBuilder(); sb.append( '[' ); int arrayLength = type.getArrayLength(obj); for ( int idx = 0; idx < arrayLength; idx++ ) { if ( idx > 0 ) { sb.append( ", " ); } sb.append( toString( type.getArrayComponent( obj, idx ) ) ); } sb.append( ']' ); return sb.toString(); } return obj.toString(); } public static void error( Object strError ) { System.out.println( strError ); throw new Error( String.valueOf( strError ) ); } }