/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.config.CommonServices; import gw.internal.gosu.parser.java.classinfo.JavaSourceDefaultValue; import gw.lang.parser.ISource; import gw.lang.parser.Keyword; import gw.lang.reflect.IAnnotatedFeatureInfo; import gw.lang.reflect.IAnnotationInfo; import gw.lang.reflect.IAttributedFeatureInfo; import gw.lang.reflect.IConstructorInfo; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IParameterInfo; import gw.lang.reflect.IPropertyInfo; import gw.lang.reflect.IRelativeTypeInfo; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeInfo; import gw.lang.reflect.ITypeRef; import gw.lang.reflect.Modifier; import gw.lang.reflect.TypeInfoUtil; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.ClassType; import gw.lang.reflect.gs.GosuClassTypeLoader; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.gs.IGosuObject; import gw.lang.reflect.gs.StringSourceFileHandle; import gw.lang.reflect.java.IJavaMethodInfo; import gw.lang.reflect.java.IJavaType; import gw.lang.reflect.java.JavaTypes; import gw.lang.reflect.module.IModule; import java.lang.reflect.Array; import java.util.List; import java.util.concurrent.Callable; /** */ public class GosuClassProxyFactory { private static final GosuClassProxyFactory INSTANCE = new GosuClassProxyFactory(); private GosuClassProxyFactory() { } public static GosuClassProxyFactory instance() { return INSTANCE; } public IGosuClassInternal create( IType type ) { if( type instanceof IJavaType ) { IJavaTypeInternal javaType = (IJavaTypeInternal)type; return (IGosuClassInternal)createJavaProxy( javaType ); } throw new IllegalArgumentException( "No handler for type: " + type.getClass().getName() ); } public IGosuClassInternal createImmediately( IType type ) { if( type.isParameterizedType() ) { type = type.getGenericType(); } if( type instanceof IJavaType ) { IJavaTypeInternal javaType = (IJavaTypeInternal)type; return (IGosuClassInternal)createJavaProxyImmediately( javaType ); } throw new IllegalArgumentException( "No handler for type: " + type.getClass().getName() ); } private IGosuClass createJavaProxy( IJavaTypeInternal type ) { if( type.isParameterizedType() ) { type = (IJavaTypeInternal)type.getGenericType(); } IGosuClassInternal gsAdapterClass; gsAdapterClass = type.getAdapterClass(); if( gsAdapterClass != null ) { return gsAdapterClass; } return createJavaProxyImmediately( type ); } private IGosuClass createJavaProxyImmediately( IJavaTypeInternal type ) { IGosuClassInternal gsAdapterClass; if( type.getEnclosingType() != null ) { // Ensure enclosing type is proxied; it contains the gosu source for the inner type IGosuClass outerProxy = (IGosuClass)TypeSystem.getByFullName( getProxyName( (IJavaType)TypeLord.getOuterMostEnclosingClass( type ) ) ); outerProxy.getInnerClasses(); if( !outerProxy.isCompilingDeclarations() ) { gsAdapterClass = (IGosuClassInternal)outerProxy.getInnerClass( type.getRelativeName().substring( type.getRelativeName().indexOf( '.' ) + 1 ) ); if( gsAdapterClass == null ) { TypeSystem.refresh( (ITypeRef)outerProxy); gsAdapterClass = (IGosuClassInternal)outerProxy.getInnerClass( type.getRelativeName().substring( type.getRelativeName().indexOf( '.' ) + 1 ) ); } } else { return null; } } else { if( type.isInterface() ) { gsAdapterClass = (IGosuClassInternal)createJavaInterfaceProxy( type ); } else { gsAdapterClass = (IGosuClassInternal)createJavaClassProxy( type ); } } type.setAdapterClass( gsAdapterClass ); if( gsAdapterClass != null ) { gsAdapterClass.setJavaType( type ); } return gsAdapterClass; } private IGosuClass createJavaInterfaceProxy( final IJavaType type ) { final IModule module = type.getTypeLoader().getModule(); GosuClassTypeLoader loader = GosuClassTypeLoader.getDefaultClassLoader( module ); return loader.makeNewClass( new LazyStringSourceFileHandle(type, new Callable<StringBuilder>() { public StringBuilder call() { TypeSystem.pushModule( module ); try { return genJavaInterfaceProxy(type); } finally { TypeSystem.popModule( module ); } } })); } private IGosuClass createJavaClassProxy( final IJavaType type ) { String strProxy = IGosuClass.PROXY_PREFIX + '.' + type.getName(); IType compilingType = GosuClassCompilingStack.getCompilingType( strProxy ); if( compilingType != null ) { return (IGosuClass)compilingType; } final IModule module = type.getTypeLoader().getModule(); return GosuClassTypeLoader.getDefaultClassLoader( module ).makeNewClass( new LazyStringSourceFileHandle( type, new Callable<StringBuilder>() { public StringBuilder call() { TypeSystem.pushModule( module ); try { return genJavaClassProxy( type ); } finally { TypeSystem.popModule( module ); } } } ) ); } private static class LazyStringSourceFileHandle extends StringSourceFileHandle { private Callable<StringBuilder> _sourceGen; public LazyStringSourceFileHandle( IType type, Callable<StringBuilder> sourceGen ) { super( getProxyName( type ), null, false, ClassType.Class ); _sourceGen = sourceGen; } @Override public ISource getSource() { if( getRawSource() == null ) { try { setRawSource( _sourceGen.call().toString() ); } catch( Exception e ) { throw new RuntimeException( e ); } } return super.getSource(); } } private static String getProxyName( IType type ) { if( type.isParameterizedType() ) { type = type.getGenericType(); } return IGosuClass.PROXY_PREFIX + '.' + type.getName(); } private StringBuilder genJavaClassProxy( IJavaType type ) { if( type.isParameterizedType() ) { type = type.getGenericType(); } StringBuilder sb = new StringBuilder(); sb.append( "package " ).append( IGosuClass.PROXY_PREFIX ).append('.').append( type.getNamespace() ).append( '\n' ); genClassImpl(type, sb); return sb; } private void genClassImpl( IJavaType type, StringBuilder sb ) { addAnnotations( type.getTypeInfo(), sb ); addModifiers(type, sb); sb.append( "class " ).append( getRelativeName( type ) ).append( '\n' ); sb.append( "{\n" ); JavaTypeInfo ti; if( type.getTypeInfo() instanceof JavaTypeInfo ) { ti = (JavaTypeInfo)type.getTypeInfo(); } else { throw new IllegalArgumentException( type + " is not a recognized Java type" ); } // Constructors for( Object o : ti.getConstructors( type ) ) { IConstructorInfo ci = (IConstructorInfo)o; genConstructor( sb, ci ); } // Properties for( Object o : ti.getProperties( type ) ) { IPropertyInfo pi = (IPropertyInfo)o; genProperty( pi, sb, type ); } // Methods for( Object o : ti.getMethods( type ) ) { IMethodInfo mi = (IMethodInfo)o; genMethodImpl( sb, mi ); } // Inner classes/interfaces for( IJavaType innerClass : type.getInnerClasses() ) { if( (Modifier.isPublic( innerClass.getModifiers() ) || Modifier.isProtected( innerClass.getModifiers() ) || !Modifier.isPrivate( innerClass.getModifiers() )) ) { if( innerClass.isInterface() ) { genInterfaceImpl( innerClass, sb ); } else { genClassImpl( innerClass, sb ); } } } sb.append( "}\n" ); } private void addModifiers(IJavaType type, StringBuilder sb) { if( type.isAbstract() ) { sb.append( "abstract " ); } if( type.getEnclosingType() != null && Modifier.isStatic(type.getModifiers()) ) { sb.append( "static " ); } if( type.isFinal() ) { sb.append( Keyword.KW_final ).append( " " ); } if( Modifier.isProtected( type.getModifiers() ) ) { sb.append( Keyword.KW_protected ).append( " " ); } else if( !Modifier.isPrivate( type.getModifiers() ) && !Modifier.isPublic( type.getModifiers() ) ) { sb.append( Keyword.KW_internal ).append( " " ); } } private void addAnnotations( IAnnotatedFeatureInfo featureInfo, StringBuilder sb ) { for( IAnnotationInfo annotation : featureInfo.getDeclaredAnnotations() ) { sb.append( makeAnnotationSource( annotation ) ).append( "\n" ); } } private static String makeAnnotationSource( IAnnotationInfo annotation ) { if( ErrorType.NAME.equals( annotation.getName() ) ) { // It's possible from IJ that the Java type is not compiled completely with incomplete or invalid annotation expressions return ""; } StringBuilder sb = new StringBuilder( "@" + annotation.getName() + "(" ); boolean bFirst = true; for( IMethodInfo mi : annotation.getType().getTypeInfo().getMethods() ) { if( isObjectMethod( mi ) ) { continue; } String name = mi.getDisplayName(); if( name.equals( "annotationType" ) ) { continue; } String value = makeValueString( annotation.getFieldValue( name ), mi.getReturnType() ); if( !bFirst ) { sb.append( ", " ); } sb.append( ":" ).append( name ).append( "=" ).append( value ); bFirst = false; } sb.append( ")" ); return sb.toString(); } public static String makeValueString( Object value, IType returnType ) { if( value == null ) { return "null"; } if( value instanceof JavaSourceDefaultValue ) { return ((JavaSourceDefaultValue)value).getValue(); } if( returnType == JavaTypes.STRING() ) { return "\"" + value.toString().replace( '\n', ' ' ) + "\""; } if( returnType.isEnum() ) { return value.toString(); } if( returnType.isPrimitive() ) { return String.valueOf( value ); } if( JavaTypes.CLASS().isAssignableFrom( returnType ) ) { if( value instanceof String ) { return (String)value; } return ((Class)value).getName(); } if( value instanceof IAnnotationInfo ) { return makeAnnotationSource( (IAnnotationInfo)value ); } if( value.getClass().isArray() ) { assert returnType.isArray(); StringBuilder arrayValue = new StringBuilder( "{" ); for( int i = 0; i < Array.getLength( value ); i++ ) { if( i > 0 ) { arrayValue.append( ", " ); } arrayValue.append( makeValueString( Array.get( value, i ), returnType.getComponentType() ) ); } arrayValue.append( "}" ); return arrayValue.toString(); } if( List.class.isAssignableFrom( value.getClass() ) ) { assert returnType.isArray(); List list = (List)value; StringBuilder arrayValue = new StringBuilder( "{" ); for( int i = 0; i < list.size(); i++ ) { if( i > 0 ) { arrayValue.append( ", " ); } arrayValue.append( makeValueString( list.get( i ), returnType.getComponentType() ) ); } arrayValue.append( "}" ); return arrayValue.toString(); } if( returnType.isArray() ) { StringBuilder arrayValue = new StringBuilder( "{" ); arrayValue.append( makeValueString( value, returnType.getComponentType() ) ); arrayValue.append( "}" ); return arrayValue.toString(); } throw new IllegalStateException(); } public static boolean isObjectMethod( IMethodInfo mi ) { IParameterInfo[] params = mi.getParameters(); IType[] paramTypes = new IType[params.length]; for( int i = 0; i < params.length; i++ ) { paramTypes[i] = params[i].getFeatureType(); } IRelativeTypeInfo ti = (IRelativeTypeInfo)JavaTypes.OBJECT().getTypeInfo(); IMethodInfo objMethod = ti.getMethod( JavaTypes.OBJECT(), mi.getDisplayName(), paramTypes ); return objMethod != null; } private String getRelativeName( IJavaType type ) { String strName = TypeSystem.getGenericRelativeName( type, false ); if( type.getEnclosingType() != null ) { int iParamsIndex = strName.indexOf( '<' ); int iIndex = iParamsIndex > 0 ? strName.substring( 0, iParamsIndex ).lastIndexOf( '.' ) : strName.lastIndexOf( '.' ); if( iIndex > 0 ) { strName = strName.substring( iIndex + 1 ); } } return strName; } private StringBuilder genJavaInterfaceProxy( IJavaType type ) { if( type.isParameterizedType() ) { type = type.getGenericType(); } StringBuilder sb = new StringBuilder(); sb.append( "package " ).append( IGosuClass.PROXY_PREFIX ).append( '.' ).append( type.getNamespace() ).append('\n'); genInterfaceImpl( type, sb ); return sb; } private void genInterfaceImpl( IJavaType type, StringBuilder sb ) { sb.append( Modifier.toModifierString( type.getModifiers() ) ).append( " interface " ).append( getRelativeName( type ) ).append('\n'); sb.append( "{\n" ); ITypeInfo ti = type.getTypeInfo(); // Interface properties for( Object o : ti.getProperties() ) { IPropertyInfo pi = (IPropertyInfo)o; genInterfacePropertyDecl( sb, pi ); } // Interface methods for( Object o : ti.getMethods() ) { IMethodInfo mi = (IMethodInfo)o; genInterfaceMethodDecl( sb, mi ); } // Inner interfaces for( IJavaType iface : type.getInnerClasses() ) { if( iface.isInterface() && (Modifier.isPublic( iface.getModifiers() ) || Modifier.isProtected( iface.getModifiers() ) || !Modifier.isPrivate( iface.getModifiers() )) && !Modifier.isFinal( iface.getModifiers() ) ) { genInterfaceImpl( iface, sb ); } } sb.append( "}\n" ); } private void genMethodImpl( StringBuilder sb, IMethodInfo mi ) { if( mi.isPrivate() ) { return; } if( mi.isStatic() && mi.getDisplayName().indexOf( '$' ) < 0 ) { genStaticMethod( sb, mi ); } else { genMemberMethod( sb, mi ); } } private void genConstructor( StringBuilder sb, IConstructorInfo ci ) { if( (ci instanceof JavaConstructorInfo) && ((JavaConstructorInfo)ci).isSynthetic() ) { return; } StringBuilder sbModifiers = appendVisibilityModifier( ci ); if( ci.getDescription() != null ) { sb.append( "\n/** " ).append( ci.getDescription() ).append( " */\n" ); } sb.append( " " ).append( sbModifiers ).append( " construct(" ); IParameterInfo[] params = getGenericParameters( ci ); for( int i = 0; i < params.length; i++ ) { IParameterInfo pi = params[i]; sb.append( ' ' ).append( "p" ).append( i ).append( " : " ).append( pi.getFeatureType().getName() ) .append( i < params.length - 1 ? ',' : ' ' ); } sb.append( ")\n" ) .append( "{\n" ) .append( "}\n" ); } private StringBuilder appendVisibilityModifier( IAttributedFeatureInfo fi ) { StringBuilder sb = new StringBuilder(); addAnnotations( fi, sb ); if( fi.isPublic() ) { // default } else if( fi.isProtected() ) { sb.append( Keyword.KW_protected ).append( " " ); } else if( fi.isPrivate() ) { sb.append( Keyword.KW_private ).append( " " ); } else // internal { sb.append( Keyword.KW_internal ).append( " " ); } return sb; } private StringBuilder appendFieldVisibilityModifier( IAttributedFeatureInfo fi ) { StringBuilder sb = new StringBuilder(); addAnnotations( fi, sb ); if( fi.isPublic() ) { sb.append( Keyword.KW_public ).append( " " ); } else if( fi.isProtected() ) { sb.append( Keyword.KW_protected ).append( " " ); } else if( !fi.isPrivate() ) { sb.append( Keyword.KW_internal ).append( " " ); } return sb; } private void genMemberMethod( StringBuilder sb, IMethodInfo mi ) { if( !canExtendMethod( mi ) ) { return; } StringBuilder sbModifiers = buildModifiers( mi ); if( mi.getDescription() != null ) { sb.append( "\n/** " ).append( mi.getDescription() ).append( " */\n" ); } sb.append( " " ).append( sbModifiers ).append( "function " ).append( mi.getDisplayName() ).append( TypeInfoUtil.getTypeVarList( mi ) ).append( "(" ); IParameterInfo[] params = getGenericParameters( mi ); for( int i = 0; i < params.length; i++ ) { IParameterInfo pi = params[i]; sb.append( ' ' ).append( "p" ).append( i ).append( " : " ).append( pi.getFeatureType().getName() ) .append( i < params.length - 1 ? ',' : ' ' ); } sb.append( ") : " ).append( getGenericReturnType( mi ).getName() ).append( "\n" ); if( !mi.isAbstract() ) { generateStub( sb, mi.getReturnType() ); } } private static void generateStub( StringBuilder sb, IType returnType ) { sb.append( "{\n" ) .append( (returnType == JavaTypes.pVOID() ? "" : " return " + (!returnType.isPrimitive() ? "null" : CommonServices.getCoercionManager().convertNullAsPrimitive( returnType, false ))) ); sb.append( "}\n" ); } private boolean canExtendMethod( IMethodInfo mi ) { if( !(mi instanceof JavaMethodInfo) ) { // It is possible that a methodinfo on a java type originates outside of java. // E.g., enhancement methods. Gosu does not support extending these. return false; } if( isPropertyMethod( mi ) ) { // We favor properties over methods -- gotta pick one return false; } int iMethodModifiers = ((IJavaMethodInfo)mi).getModifiers(); return //!java.lang.reflect.Modifier.isFinal( iMethodModifiers ) && !java.lang.reflect.Modifier.isNative( iMethodModifiers ) && // See GosuClassInstanceFactory.genSuperClassMembers() (we don't allow finalizers) !mi.getDisplayName().equals( "finalize" ) && mi.getDisplayName().indexOf( '$' ) < 0; } private void genStaticMethod( StringBuilder sb, IMethodInfo mi ) { if( !(mi instanceof JavaMethodInfo) ) { // It is possible that a methodinfo on a java type originates outside of java. // E.g., enhancement methods. Only static JAVA members should be reflected in // the proxy. return; } if( isPropertyMethod( mi ) ) { // We favor properties over methods -- gotta pick one return; } StringBuilder sbModifiers = appendVisibilityModifier( mi ); if( mi.getDescription() != null ) { sb.append( "\n/** " ).append( mi.getDescription() ).append( " */\n" ); } sb.append( " " ).append( sbModifiers ).append( "static function " ).append( mi.getDisplayName() ).append( TypeInfoUtil.getTypeVarList( mi ) ).append( "(" ); IParameterInfo[] params = getGenericParameters( mi ); for( int i = 0; i < params.length; i++ ) { IParameterInfo pi = params[i]; sb.append( ' ' ).append( "p" ).append( i ).append( " : " ).append( pi.getFeatureType().getName() ) .append( i < params.length - 1 ? ',' : ' ' ); } sb.append( ") : " ).append( getGenericReturnType( mi ).getName() ).append( "\n" ); generateStub( sb, mi.getReturnType() ); } private void genInterfaceMethodDecl( StringBuilder sb, IMethodInfo mi ) { if( !(mi instanceof JavaMethodInfo) ) { // It is possible that a methodinfo on a java type originates outside of java. // E.g., enhancement methods. Gosu does not support extending these. return; } if( isPropertyMethod( mi ) ) { return; } if( mi.getDisplayName().equals( "hashCode" ) || mi.getDisplayName().equals( "equals" ) || mi.getDisplayName().equals( "toString" ) ) { if( !mi.getOwnersType().getName().equals( IGosuObject.class.getName() ) ) { return; } } if( mi.getDescription() != null ) { sb.append( "\n/** " ).append( mi.getDescription() ).append( " */\n" ); } sb.append( " function " ).append( mi.getDisplayName() ).append( TypeInfoUtil.getTypeVarList( mi ) ).append( "(" ); IParameterInfo[] params = getGenericParameters( mi ); for( int i = 0; i < params.length; i++ ) { IParameterInfo pi = params[i]; sb.append( ' ' ).append( "p" ).append( i ).append( " : " ).append( pi.getFeatureType().getName() ); sb.append( i < params.length - 1 ? ',' : ' ' ); } sb.append( ") : " ).append( getGenericReturnType( mi ).getName() ).append( ";\n" ); } public static boolean isPropertyMethod( IMethodInfo mi ) { return isPropertyGetter( mi ) || isPropertySetter( mi ); } public static boolean isPropertyGetter( IMethodInfo mi ) { return isPropertyGetter( mi, "get" ) || isPropertyGetter( mi, "is" ); } public static boolean isPropertySetter( IMethodInfo mi ) { String strMethod = mi.getDisplayName(); if( strMethod.startsWith( "set" ) && strMethod.length() > 3 && mi.getParameters().length == 1 && mi.getReturnType() == JavaTypes.pVOID() ) { String strProp = strMethod.substring( "set".length() ); if( Character.isUpperCase( strProp.charAt( 0 ) ) ) { ITypeInfo ti = (ITypeInfo)mi.getContainer(); IPropertyInfo pi = ti instanceof IRelativeTypeInfo ? ((IRelativeTypeInfo)ti).getProperty( mi.getOwnersType(), strProp ) : ti.getProperty( strProp ); if( pi != null && pi.isReadable() && mi.isStatic() == pi.isStatic() && getGenericType( pi ).getName().equals( getGenericParameters( mi )[0].getFeatureType().getName() ) ) { return !Keyword.isKeyword( pi.getName() ) || Keyword.isValueKeyword( pi.getName() ); } } } return false; } public static boolean isPropertyGetter( IMethodInfo mi, String strPrefix ) { String strMethod = mi.getDisplayName(); if( strMethod.startsWith( strPrefix ) && mi.getParameters().length == 0 ) { String strProp = strMethod.substring( strPrefix.length() ); if( strProp.length() > 0 && Character.isUpperCase( strProp.charAt( 0 ) ) ) { ITypeInfo ti = (ITypeInfo)mi.getContainer(); IPropertyInfo pi = ti instanceof IRelativeTypeInfo ? ((IRelativeTypeInfo)ti).getProperty( mi.getOwnersType(), strProp ) : ti.getProperty( strProp ); if( pi != null && getGenericType( pi ).getName().equals( getGenericReturnType( mi ).getName() ) ) { return !Keyword.isKeyword( pi.getName() ) || Keyword.isValueKeyword( pi.getName() ); } } } return false; } private void genInterfacePropertyDecl( StringBuilder sb, IPropertyInfo pi ) { if( pi.isStatic() ) { genStaticProperty( pi, sb ); return; } if( !pi.isReadable() ) { return; } if( !(pi instanceof JavaBaseFeatureInfo) ) { // It is possible that a methodinfo on a java type originates outside of java. // E.g., enhancement methods. Gosu does not support extending these. return; } IType type = getGenericType( pi ); if( pi.getDescription() != null ) { sb.append( "\n/** " ).append( pi.getDescription() ).append( " */\n" ); } sb.append( " property get " ).append( pi.getName() ).append( "() : " ).append( type.getName() ).append( "\n" ); if( pi.isWritable( pi.getOwnersType() ) ) { sb.append( " property set " ).append( pi.getName() ).append( "( _proxy_arg_value : " ).append( type.getName() ).append( " )\n" ); } } private void genProperty( IPropertyInfo pi, StringBuilder sb, IJavaType type ) { if( pi.isPrivate() ) { return; } if( pi.isStatic() ) { genStaticProperty( pi, sb ); } else { genMemberProperty( pi, sb, type ); } } private void genMemberProperty( IPropertyInfo pi, StringBuilder sb, IJavaType type ) { if( pi.isStatic() ) { return; } if( !(pi instanceof JavaBaseFeatureInfo) ) { // It is possible that a propertyinfo on a java type originates outside of java. // E.g., enhancement properties. Gosu does not support extending these. return; } if( Keyword.isKeyword( pi.getName() ) && !Keyword.isValueKeyword( pi.getName() ) ) { // Sorry these won't compile //## todo: handle them reflectively? return; } if( pi instanceof JavaFieldPropertyInfo ) { StringBuilder sbModifiers = appendFieldVisibilityModifier( pi ); sb.append( " " ).append( sbModifiers ).append( " var " ).append( pi.getName() ).append( " : " ).append( getGenericType( pi ).getName() ).append( "\n" ); } else { IMethodInfo mi = getPropertyGetMethod( pi, type ); boolean bFinal = false; if( mi != null ) { int iMethodModifiers = ((IJavaMethodInfo)mi).getModifiers(); bFinal = java.lang.reflect.Modifier.isFinal( iMethodModifiers ); } if( mi != null && !bFinal ) { if( mi.getDescription() != null ) { sb.append( "\n/** " ).append( mi.getDescription() ).append( " */\n" ); } StringBuilder sbModifiers = buildModifiers( mi ); sb.append( " " ).append( sbModifiers ).append( "property get " ).append( pi.getName() ).append( "() : " ).append( getGenericType( pi ).getName() ).append( "\n" ); if( !mi.isAbstract() ) { generateStub( sb, mi.getReturnType() ); } } else { StringBuilder sbModifiers; boolean bAbstact = false; if( bFinal ) { bAbstact = mi.isAbstract(); sbModifiers = buildModifiers( mi ); } else { sbModifiers = appendVisibilityModifier( pi ); } sb.append( " " ).append( sbModifiers ).append( "property get " ).append( pi.getName() ).append( "() : " ).append( getGenericType( pi ).getName() ).append( "\n" ); if( !bAbstact ) { generateStub( sb, getGenericType( pi ) ); } } mi = getPropertySetMethod( pi, type ); bFinal = false; if( mi != null ) { int iMethodModifiers = ((IJavaMethodInfo)mi).getModifiers(); bFinal = java.lang.reflect.Modifier.isFinal( iMethodModifiers ); } if( mi != null && !bFinal ) { StringBuilder sbModifiers = buildModifiers( mi ); if( pi.isWritable( pi.getOwnersType() ) ) { sb.append( " " ).append( sbModifiers ).append( "property set " ).append( pi.getName() ).append( "( _proxy_arg_value : " ).append( getGenericType( pi ).getName() ).append( " )\n" ); if( !mi.isAbstract() ) { generateStub( sb, JavaTypes.pVOID() ); } } } else { if( pi.isWritable( type.getEnclosingType() != null ? null : pi.getOwnersType() ) ) { StringBuilder sbModifiers; boolean bAbstact = false; if( bFinal ) { bAbstact = mi.isAbstract(); sbModifiers = buildModifiers( mi ); } else { sbModifiers = appendVisibilityModifier( pi ); } sb.append( " " ).append( sbModifiers ).append( "property set " ).append( pi.getName() ).append( "( _proxy_arg_value : " ).append( getGenericType( pi ).getName() ).append( " )\n" ); if( !bAbstact ) { generateStub( sb, JavaTypes.pVOID() ); } } } } } private StringBuilder buildModifiers( IAttributedFeatureInfo fi ) { StringBuilder sbModifiers = new StringBuilder(); addAnnotations( fi, sbModifiers ); if( fi.isAbstract() ) { sbModifiers.append( Keyword.KW_abstract ).append( " " ); } else if( fi.isFinal() ) { sbModifiers.append( Keyword.KW_final ).append( " " ); } if( fi.isProtected() ) { sbModifiers.append( Keyword.KW_protected ).append( " " ); } else if( !fi.isPrivate() && !fi.isPublic() ) { sbModifiers.append( Keyword.KW_internal ).append( " " ); } return sbModifiers; } private IMethodInfo getPropertyGetMethod( IPropertyInfo pi, IJavaType ownerType ) { JavaTypeInfo ti; if( !(pi.getOwnersType() instanceof IJavaType) ) { return null; } if( ownerType.getTypeInfo() instanceof JavaTypeInfo ) { ti = (JavaTypeInfo)ownerType.getTypeInfo(); } else { throw new IllegalArgumentException( ownerType + " is not a recognized Java type" ); } IType propType = pi.getFeatureType(); String strAccessor = "get" + pi.getDisplayName(); IMethodInfo mi = ti.getMethod( ownerType, strAccessor ); if( mi == null || mi.getReturnType() != propType ) { strAccessor = "is" + pi.getDisplayName(); mi = ti.getMethod( ownerType, strAccessor ); } if( mi != null && mi.getReturnType() == propType ) { return mi; } return null; } private IMethodInfo getPropertySetMethod( IPropertyInfo pi, IJavaType ownerType ) { JavaTypeInfo ti; if( !(pi.getOwnersType() instanceof IJavaType) ) { return null; } if( ownerType.getTypeInfo() instanceof JavaTypeInfo ) { ti = (JavaTypeInfo)ownerType.getTypeInfo(); } else { throw new IllegalArgumentException( ownerType + " is not a recognized Java type" ); } IType propType = pi.getFeatureType(); // Check for Setter String strAccessor = "set" + pi.getDisplayName(); IMethodInfo mi = ti.getMethod( ownerType, strAccessor, propType ); if( mi != null && mi.getReturnType() == JavaTypes.pVOID() ) { return mi; } return null; } private void genStaticProperty( IPropertyInfo pi, StringBuilder sb ) { if( !pi.isStatic() ) { return; } if( !(pi instanceof JavaBaseFeatureInfo) ) { // It is possible that a methodinfo on a java type originates outside of java. // E.g., enhancement methods. Gosu does not support extending these. return; } if( Keyword.isKeyword( pi.getName() ) ) { // Sorry these won't compile //## todo: handle them reflectively? return; } if( pi.getDescription() != null ) { sb.append( "\n/** " ).append( pi.getDescription() ).append( " */\n" ); } if( pi instanceof JavaFieldPropertyInfo ) { StringBuilder sbModifiers = appendFieldVisibilityModifier( pi ); sb.append( " " ).append( sbModifiers ).append( "static var " ).append( pi.getName() ).append( " : " ).append( getGenericType( pi ).getName() ).append( "\n" ); } else { StringBuilder sbModifiers = appendVisibilityModifier( pi ); sb.append( " " ).append( sbModifiers ).append( "static property get " ).append( pi.getName() ).append( "() : " ).append( getGenericType( pi ).getName() ).append( "\n" ); if( !pi.isAbstract() ) { generateStub( sb, getGenericType( pi ) ); } if( pi.isWritable( pi.getOwnersType() ) ) { sb .append( " static property set " ).append( pi.getName() ).append( "( _proxy_arg_value : " ).append( getGenericType( pi ).getName() ).append( " )\n" ); if( !pi.isAbstract() ) { generateStub( sb, JavaTypes.pVOID() ); } } } } private static IType getGenericType( IPropertyInfo pi ) { return (pi instanceof JavaPropertyInfo) ? ((JavaPropertyInfo)pi).getGenericIntrinsicType() : pi.getFeatureType(); } public static IType getGenericReturnType( IMethodInfo mi ) { return (mi instanceof JavaMethodInfo) ? ((JavaMethodInfo)mi).getGenericReturnType() : mi.getReturnType(); } public static IParameterInfo[] getGenericParameters( IMethodInfo mi ) { return (mi instanceof JavaMethodInfo) ? ((JavaMethodInfo)mi).getGenericParameters() : mi.getParameters(); } private static IParameterInfo[] getGenericParameters( IConstructorInfo ci ) { return (ci instanceof JavaConstructorInfo) ? ((JavaConstructorInfo)ci).getGenericParameters() : ci.getParameters(); } }