package fr.lteconsulting.hexa.revrpc.rebind;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
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.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
public class CallDeserializerGenerator extends Generator
{
// Context and logger for code generation
TreeLogger logger = null;
GeneratorContext context = null;
TypeOracle typeOracle = null;
// asked type name
String typeName = null;
// type info on the asked class
JClassType classType = null;
// package of the asked type
String packageName = null;
// generated class name
String className = null;
String implementationClass = "Void";
JClassType implementationType = null;
@Override
public String generate( TreeLogger logger, GeneratorContext context, String typeName ) throws UnableToCompleteException
{
this.logger = logger;
this.context = context;
this.typeName = typeName;
// get the "reflection" machine of GWT compiler
typeOracle = context.getTypeOracle();
try
{
// get classType and save instance variables
classType = typeOracle.getType( typeName );
packageName = classType.getPackage().getName();
className = classType.getSimpleSourceName() + "Impl";
// Generate class source code
generateClass();
}
catch( Exception e )
{
// record to logger that Map generation threw an exception
logger.log( TreeLogger.ERROR, "ERROR when generating " + className + " for " + typeName, e );
}
// return the fully qualifed name of the class generated
return packageName + "." + className;
}
private void msg( String message )
{
logger.log( TreeLogger.INFO, message, null );
}
private void generateClass()
{
deserializers = new HashMap<String, DeserializerInfo>();
toGenerate = new ArrayList<String>();
// get print writer that receives the source code
PrintWriter printWriter = null;
printWriter = context.tryCreate( logger, packageName, className );
// 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, className );
// output a class "typeName" + "Impl"
// which extends the asked type
composer.addImplementedInterface( typeName );
composer.addImport( "java.util.List" );
composer.addImport( "java.util.ArrayList" );
composer.addImport( "java.util.Date" );
composer.addImport( "com.google.gwt.json.client.JSONObject" );
composer.addImport( "com.google.gwt.json.client.JSONValue" );
composer.addImport( "com.google.gwt.json.client.JSONArray" );
composer.addImport( "com.dng.revrpc.client.CallDeserializerUtil" );
msg( "Requested type : " + classType.getParameterizedQualifiedSourceName() );
JClassType[] implementedInterfaces = classType.getImplementedInterfaces();
for( int i = 0; i < implementedInterfaces.length; i++ )
msg( "Implemented interface : " + implementedInterfaces[i].getParameterizedQualifiedSourceName() );
JParameterizedType parametrizedType = implementedInterfaces[0].isParameterized();
implementationType = parametrizedType.getTypeArgs()[0];
implementationClass = implementationType.getParameterizedQualifiedSourceName();
msg( "Implementation type : " + implementationClass );
SourceWriter sourceWriter = composer.createSourceWriter( context, printWriter );
// generate the List<String> getMethods(); method
generateRegisterMethod( sourceWriter );
generateNewCallMethod( sourceWriter );
generateDeserializers( sourceWriter );
// close generated class
sourceWriter.outdent();
sourceWriter.println( "}" );
// commit generated class
context.commit( logger, printWriter );
}
private void generateRegisterMethod( SourceWriter sourceWriter )
{
sourceWriter.println( "private " + implementationClass + " implementation = null;" );
sourceWriter.println();
sourceWriter.println( "@Override" );
sourceWriter.println( "public void registerImplementation( " + implementationClass + " implementation )" );
sourceWriter.println( "{" );
sourceWriter.indent();
sourceWriter.println( "this.implementation = implementation;" );
sourceWriter.outdent();
sourceWriter.println( "}" );
sourceWriter.println();
}
private String arrayToString( ArrayList<String> list )
{
String res = "";
boolean addComa = false;
for( String s : list )
{
if( addComa )
res += ", ";
addComa = true;
res += s;
}
return res;
}
static class DeserializerInfo
{
String methodName;
JType serializedType;
}
HashMap<String, DeserializerInfo> deserializers = new HashMap<String, DeserializerInfo>();
ArrayList<String> toGenerate = new ArrayList<String>();
private void generateNewCallMethod( SourceWriter sourceWriter )
{
sourceWriter.println( "@Override" );
sourceWriter.println( "public void newCall( JSONObject json )" );
sourceWriter.println( "{" );
sourceWriter.indent();
sourceWriter.println( "String method = json.get( \"method\" ).isString().stringValue();" );
JMethod[] methods = implementationType.getMethods();
for( int m = 0; m < methods.length; m++ )
{
JMethod method = methods[m];
sourceWriter.println( "if( method.equals(\"" + method.getName() + "\") )" );
sourceWriter.println( "{" );
sourceWriter.indent();
JParameter[] parameters = method.getParameters();
ArrayList<String> pNames = new ArrayList<String>();
for( int p = 0; p < parameters.length; p++ )
{
JParameter prm = parameters[p];
pNames.add( prm.getName() );
sourceWriter.println( "JSONValue " + prm.getName() + "Json = CallDeserializerUtil.getParam( json, " + p + ", \"" + prm.getType().getParameterizedQualifiedSourceName() + "\" );" );
String deserializerMethodName = getDeserializerMethodName( prm.getType() );
sourceWriter.println( prm.getType().getParameterizedQualifiedSourceName() + " " + prm.getName() + " = " + deserializerMethodName + "( " + prm.getName() + "Json.isObject().get( \"value\" ) );" );
}
// make the call
sourceWriter.println();
sourceWriter.println( "implementation." + method.getName() + "(" + arrayToString( pNames ) + ");" );
sourceWriter.outdent();
sourceWriter.println( "}" );
}
sourceWriter.outdent();
sourceWriter.println( "}" );
sourceWriter.println();
}
private String getDeserializerMethodName( JType type )
{
DeserializerInfo info = deserializers.get( type.getParameterizedQualifiedSourceName() );
if( info == null )
{
info = new DeserializerInfo();
info.methodName = "deserialize_" + deserializers.size() + "_" + type.getSimpleSourceName();
info.serializedType = type;
deserializers.put( type.getParameterizedQualifiedSourceName(), info );
toGenerate.add( type.getParameterizedQualifiedSourceName() );
msg( "NEED FOR DESERIALIZER FOR TYPE " + type.getParameterizedQualifiedSourceName() );
}
return info.methodName;
}
private void generateDeserializers( SourceWriter sourceWriter )
{
while( !toGenerate.isEmpty() )
{
String typeName = toGenerate.remove( 0 );
msg( "GENERATING DESERIALIZER FOR TYPE " + typeName );
DeserializerInfo info = deserializers.get( typeName );
JType type = info.serializedType;
generateDeserializer( sourceWriter, info.methodName, type );
}
}
private void generateDeserializer( SourceWriter sourceWriter, String methodName, JType type )
{
generateDeserializerBegin( sourceWriter, methodName, type );
if( type.getParameterizedQualifiedSourceName().equals( "java.lang.String" ) )
sourceWriter.println( "return json.isString().stringValue();" );
else if( type.getParameterizedQualifiedSourceName().equals( "java.lang.Integer" ) )
sourceWriter.println( "return (Integer) json.isNumber().doubleValue();" );
else if( type.getParameterizedQualifiedSourceName().equals( "int" ) )
sourceWriter.println( "return (int) json.isNumber().doubleValue();" );
else if( type.getParameterizedQualifiedSourceName().equals( "java.lang.Boolean" ) )
sourceWriter.println( "return (Boolean) json.isBoolean().booleanValue();" );
else if( type.getParameterizedQualifiedSourceName().equals( "boolean" ) )
sourceWriter.println( "return (boolean) json.isBoolean().booleanValue();" );
else if( type.getParameterizedQualifiedSourceName().equals( "java.util.Date" ) )
sourceWriter.println( "return new Date( json.isString().stringValue() );" );
else if( type.getQualifiedSourceName().equals( "java.util.List" ) )
{
JParameterizedType parametrizedType = type.isParameterized();
JClassType itemsType = parametrizedType.getTypeArgs()[0];
sourceWriter.println( "java.util.ArrayList res = new java.util.ArrayList();" );
sourceWriter.println( "JSONArray jsonArray = json.isArray();" );
sourceWriter.println( "for( int i=0; i<jsonArray.size(); i++ )" );
sourceWriter.indent();
sourceWriter.println( "res.add( " + getDeserializerMethodName( itemsType ) + "( jsonArray.get( i ) ) );" );
sourceWriter.outdent();
sourceWriter.println( "return res;" );
}
else if( type.isEnum() != null )
{
// JEnumType type.isEnum();
sourceWriter.println( "return " + type.getParameterizedQualifiedSourceName() + ".valueOf( json.isString().stringValue() );" );
}
else
{
sourceWriter.println( type.getParameterizedQualifiedSourceName() + " res = new " + type.getParameterizedQualifiedSourceName() + "();" );
JField fields[] = type.isClass().getFields();
for( int i = 0; i < fields.length; i++ )
{
JField field = fields[i];
String deserializerMethodName = getDeserializerMethodName( field.getType() );
sourceWriter.println( field.getType().getParameterizedQualifiedSourceName() + " " + field.getName() + " = " + deserializerMethodName + "( json.isObject().get( \"" + field.getName() + "\" ) );" );
sourceWriter.println( "res." + field.getName() + " = " + field.getName() + ";" );
}
sourceWriter.println( "return res;" );
}
generateDeserializerEnd( sourceWriter );
}
private void generateDeserializerBegin( SourceWriter sourceWriter, String methodName, JType type )
{
sourceWriter.println( "public " + type.getParameterizedQualifiedSourceName() + " " + methodName + "( JSONValue json )" );
sourceWriter.println( "{" );
sourceWriter.indent();
}
private void generateDeserializerEnd( SourceWriter sourceWriter )
{
sourceWriter.outdent();
sourceWriter.println( "}" );
}
/*
* private void generateGetMethods( SourceWriter sourceWriter ) { // check
* what are the methods of "typeName" JMethod[] methods =
* classType.getMethods();
*
* // maybe should use a better name so that it cannot possibly conflict
* with the extended class
* sourceWriter.println("private ArrayList<String> _methods = null;");
* sourceWriter.println("public List<String> getMethods()");
* sourceWriter.println("{"); sourceWriter.indent();
*
* sourceWriter.println("if( _methods != null ) return _methods;");
* sourceWriter.println("_methods = new ArrayList<String>();"); for( int
* i=0; i< methods.length; i++ ) {
* sourceWriter.println("_methods.add( \""+methods[i].getName()+"\" );"); }
* sourceWriter.outdent(); sourceWriter.println("return _methods;");
* sourceWriter.println("}"); }
*/
}