package fr.lteconsulting.hexa.rebind.classinfo; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JField; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JParameter; import com.google.gwt.core.ext.typeinfo.JPrimitiveType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; class ClazzInfoBuilder { TreeLogger logger; GeneratorContext context; JClassType reflectedType; String reflectedTypeName; String packageName; String generatedClassName; public ClazzInfoBuilder(TreeLogger logger, GeneratorContext context) { this.logger = logger; this.context = context; } public String buildClassInfoFor(JClassType reflectedType) { this.reflectedType = reflectedType; reflectedTypeName = reflectedType.getParameterizedQualifiedSourceName(); if( reflectedTypeName.equals( "com.google.gwt.core.client.JavaScriptObject" ) ) return "fr.lteconsulting.hexa.classinfo.gwt.internal.JavaScriptObjectClazz"; if( reflectedTypeName.equals( "java.lang.Object" ) ) return "fr.lteconsulting.hexa.classinfo.internal.ObjectClazz"; packageName = reflectedType.getPackage().getName(); generatedClassName = reflectedType.getSimpleSourceName() + "ClazzImpl"; // Generate class source code buildClassInfo(); // return the fully qualifed name of the class generated return packageName + "." + generatedClassName; } private void buildClassInfo() { // get print writer that receives the source code PrintWriter printWriter; printWriter = context.tryCreate( logger, packageName, generatedClassName ); // print writer if null, source code has ALREADY been generated, return if( printWriter == null ) return; // init composer, set class properties, create source writer ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory( packageName, generatedClassName ); // output a class "typeName" + "Impl" // which extends the asked type composer.setSuperclass( "fr.lteconsulting.hexa.classinfo.internal.ClazzBase<" + reflectedTypeName + ">" ); composer.addImport( "java.util.List" ); composer.addImport( "java.util.ArrayList" ); composer.addImport( "fr.lteconsulting.hexa.classinfo.Field" ); composer.addImport( "fr.lteconsulting.hexa.classinfo.Method" ); SourceWriter sourceWriter = composer.createSourceWriter( context, printWriter ); // generate the List<String> getMethods(); method buildClassInfo(sourceWriter); // close generated class sourceWriter.outdent(); sourceWriter.println( "}" ); // commit generated class context.commit( logger, printWriter ); } private void buildClassInfo(SourceWriter sourceWriter) { sourceWriter.println( "" ); String superClassName; String superclassGeneratedClazz; JClassType superClass = reflectedType.getSuperclass(); if( superClass != null ) { superClassName = superClass.getQualifiedSourceName() + ".class"; ClazzInfoBuilder superclassGenerator = new ClazzInfoBuilder( logger, context ); superclassGeneratedClazz = superclassGenerator.buildClassInfoFor(reflectedType.getSuperclass()); } else { superClassName = "null"; superclassGeneratedClazz = null; } sourceWriter.println( "public " + generatedClassName + "()" ); sourceWriter.println( "{" ); sourceWriter.indent(); sourceWriter.println( "super( " + reflectedType.getQualifiedSourceName() + ".class, \"" + reflectedType.getSimpleSourceName() + "\", " + superClassName + " );" ); sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); sourceWriter.println( "protected void _ensureSuperClassInfoRegistered()" ); sourceWriter.println( "{" ); sourceWriter.indent(); if( superclassGeneratedClazz != null ) sourceWriter.println( "fr.lteconsulting.hexa.classinfo.ClassInfo.RegisterClazz( new " + superclassGeneratedClazz + "() );" ); sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); // Fields List<String> fieldClassNames = new ArrayList<String>(); for( JField field : reflectedType.getFields() ) { if( field.isStatic() ) continue; // skip String fieldClassName = field.getName() + "_FieldImpl"; fieldClassNames.add( fieldClassName ); buildFieldClass(field, sourceWriter); } sourceWriter.println( "protected List<Field> _getDeclaredFields()" ); sourceWriter.println( "{" ); sourceWriter.indent(); sourceWriter.println( "ArrayList<Field> res = new ArrayList<Field>();" ); for( String fieldClassName : fieldClassNames ) sourceWriter.println( "res.add( new " + fieldClassName + "());" ); sourceWriter.outdent(); sourceWriter.println( "return res;" ); sourceWriter.println( "}" ); sourceWriter.println( "" ); // Methods List<String> methodClassNames = new ArrayList<String>(); for( JMethod method : reflectedType.getMethods() ) { String methodClassName = method.getName() + "_MethodImpl"; while( methodClassNames.contains( methodClassName ) ) methodClassName += "_"; methodClassNames.add( methodClassName ); generateMethodClass( methodClassName, method, sourceWriter ); } sourceWriter.println( "protected List<Method> _getMethods()" ); sourceWriter.println( "{" ); sourceWriter.indent(); sourceWriter.println( "ArrayList<Method> res = new ArrayList<Method>();" ); for( String methodClassName : methodClassNames ) sourceWriter.println( "res.add( new " + methodClassName + "());" ); sourceWriter.outdent(); sourceWriter.println( "return res;" ); sourceWriter.println( "}" ); sourceWriter.println( "" ); // New sourceWriter.println( "@Override" ); sourceWriter.println( "public " + reflectedTypeName + " NEW()" ); sourceWriter.println( "{" ); sourceWriter.indent(); if( reflectedType.isAbstract() ) { sourceWriter.println( "throw new IllegalArgumentException( \"Targeted class is abstract, cannot create instance\" );" ); } else { try { reflectedType.getConstructor( new JType[] {} ); sourceWriter.println( "return new " + reflectedType.getQualifiedSourceName() + "();" ); } catch( NotFoundException e ) { sourceWriter.println( "throw new IllegalArgumentException( \"Targeted class does not have a zero argument constructor, cannot create instance\" );" ); } } sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); } private static class ModifierBuilder { StringBuilder sb = new StringBuilder(); boolean empty = true; public void append( String s ) { if( !empty ) sb.append( " & " ); empty = false; sb.append( s ); } @Override public String toString() { String res = sb.toString(); if( res.isEmpty() ) return "0"; return res; } } private String getFieldModifier( JField field ) { ModifierBuilder mb = new ModifierBuilder(); if( field.isPrivate() ) mb.append( "2" );//"java.lang.reflect.Modifier.PRIVATE" ); if( field.isProtected() ) mb.append( "4" );//"java.lang.reflect.Modifier.PROTECTED" ); if( field.isPublic() ) mb.append( "1" );//"java.lang.reflect.Modifier.PUBLIC" ); if( field.isStatic() ) mb.append( "8" );//"java.lang.reflect.Modifier.STATIC" ); if( field.isTransient() ) mb.append( "128" );//"java.lang.reflect.Modifier.TRANSIENT" ); if( field.isVolatile() ) mb.append( "64" );//"java.lang.reflect.Modifier.VOLATILE" ); if( field.isFinal() ) mb.append( "16" );//"java.lang.reflect.Modifier.FINAL" ); return mb.toString(); } private void buildFieldClass(JField field, SourceWriter sourceWriter) { String fieldClassName = field.getName() + "_FieldImpl"; sourceWriter.println( "static class " + fieldClassName + " extends fr.lteconsulting.hexa.classinfo.internal.FieldBase {" ); sourceWriter.indent(); sourceWriter.println( "public " + fieldClassName + "()" ); sourceWriter.println( "{" ); sourceWriter.indent(); sourceWriter.println( "super(" + field.getType().getQualifiedSourceName() + ".class, \"" + field.getName() + "\", " + getFieldModifier( field ) + ");" ); sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); sourceWriter.println( "@Override" ); sourceWriter.println( "public void setValue( Object object, Object value )" ); sourceWriter.println( "{" ); sourceWriter.indent(); if( field.getType().isPrimitive() != null ) sourceWriter.println( "setValueInternal_int( object, (Integer) value );" ); else sourceWriter.println( "setValueInternal_Object( object, value );" ); sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); sourceWriter.println( "@Override" ); sourceWriter.println( "public <OUT> OUT getValue( Object object )" ); sourceWriter.println( "{" ); sourceWriter.indent(); if( field.getType().isPrimitive() != null ) sourceWriter.println( "return (OUT) (Integer) getValueInternal_int( object );" ); else sourceWriter.println( "return (OUT) getValueInternal_Object( object );" ); sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); sourceWriter.println( "@Override public native final void copyValueTo( Object source, Object destination )" ); sourceWriter.println( "/*-{" ); sourceWriter.indent(); sourceWriter.println( "destination.@" + reflectedType.getQualifiedSourceName() + "::" + field.getName() + " = source.@" + reflectedType.getQualifiedSourceName() + "::" + field.getName() + ";" ); sourceWriter.outdent(); sourceWriter.println( "}-*/;" ); sourceWriter.println( "" ); sourceWriter.println( "private native final void setValueInternal_int( Object object, int value )" ); sourceWriter.println( "/*-{" ); sourceWriter.indent(); sourceWriter.println( "object.@" + reflectedType.getQualifiedSourceName() + "::" + field.getName() + " = value;" ); sourceWriter.outdent(); sourceWriter.println( "}-*/;" ); sourceWriter.println( "" ); sourceWriter.println( "private native final int getValueInternal_int( Object object )" ); sourceWriter.println( "/*-{" ); sourceWriter.indent(); sourceWriter.println( "return object.@" + reflectedType.getQualifiedSourceName() + "::" + field.getName() + ";" ); sourceWriter.outdent(); sourceWriter.println( "}-*/;" ); sourceWriter.println( "" ); sourceWriter.println( "private native final void setValueInternal_Object( Object object, Object value )" ); sourceWriter.println( "/*-{" ); sourceWriter.indent(); sourceWriter.println( "object.@" + reflectedType.getQualifiedSourceName() + "::" + field.getName() + " = value;" ); sourceWriter.outdent(); sourceWriter.println( "}-*/;" ); sourceWriter.println( "" ); sourceWriter.println( "private native final Object getValueInternal_Object( Object object )" ); sourceWriter.println( "/*-{" ); sourceWriter.indent(); sourceWriter.println( "return object.@" + reflectedType.getQualifiedSourceName() + "::" + field.getName() + ";" ); sourceWriter.outdent(); sourceWriter.println( "}-*/;" ); sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); } private void generateMethodClass( String methodClassName, JMethod method, SourceWriter sourceWriter ) { JParameter[] params = method.getParameters(); StringBuilder sb = new StringBuilder(); sb.append( "new Class<?>[] {" ); for( int p = 0; p < params.length; p++ ) { if( p > 0 ) sb.append( ", " ); sb.append( params[p].getType().getErasedType().getQualifiedSourceName() ); sb.append( ".class" ); } sb.append( "}" ); sourceWriter.println( "static class " + methodClassName + " extends fr.lteconsulting.hexa.classinfo.internal.MethodBase {" ); sourceWriter.indent(); sourceWriter.println( "public " + methodClassName + "()" ); sourceWriter.println( "{" ); sourceWriter.indent(); sourceWriter.println( "super(" + method.getReturnType().getErasedType().getQualifiedSourceName() + ".class, \"" + method.getName() + "\", " + sb.toString() + ");" ); sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); sourceWriter.println( "@Override" ); sourceWriter.println( "public Object invoke( Object target, Object... parameters )" ); sourceWriter.println( "{" ); sourceWriter.indent(); if( method.isPrivate() ) { sourceWriter.println( "throw new java.lang.RuntimeException(\"Cannot call private method " + method.getName() + "\" );" ); } else { sourceWriter.println( "try {" ); sourceWriter.indent(); sb = new StringBuilder(); if( method.getReturnType().getSimpleSourceName().equals( "void" ) ) sb.append( "((" + reflectedType.getQualifiedSourceName() + ") target)." + method.getName() + "(" ); else sb.append( "return (Object) ((" + reflectedType.getQualifiedSourceName() + ") target)." + method.getName() + "(" ); for( int p = 0; p < params.length; p++ ) { if( p > 0 ) sb.append( ", " ); JPrimitiveType primitive = params[p].getType().isPrimitive(); if( primitive != null ) { String boxedCast = primitive.getQualifiedBoxedSourceName(); sb.append( "(" + params[p].getType().getQualifiedSourceName() + ") (" + boxedCast + ") parameters[" + p + "]" ); } else { sb.append( "(" + params[p].getType().getErasedType().getQualifiedSourceName() + ") parameters[" + p + "]" ); } } sb.append( ");" ); sourceWriter.println( sb.toString() ); if( method.getReturnType().getSimpleSourceName().equals( "void" ) ) sourceWriter.println( "return null;" ); sourceWriter.outdent(); sourceWriter.println( "} catch( Throwable e ) {" ); sourceWriter.indent(); sourceWriter.println( "throw new java.lang.RuntimeException(\"Called method raised an exception " + method.getName() + " : \" + e.getMessage() );" ); sourceWriter.outdent(); sourceWriter.println( "}" ); } sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); sourceWriter.outdent(); sourceWriter.println( "}" ); sourceWriter.println( "" ); } }