package fr.lteconsulting.hexa.rebind.classinfo;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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.JMethod;
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;
import fr.lteconsulting.hexa.classinfo.gwt.ClazzBundle;
import fr.lteconsulting.hexa.classinfo.gwt.ReflectedClasses;
public class ClazzBundleGenerator extends Generator
{
// Context and logger for code generation
TreeLogger logger;
GeneratorContext context;
TypeOracle typeOracle;
// asked type name
String askedTypeName;
// type info on the asked class
JClassType askedType;
Set<JType> introspectedTypes;
JMethod registerMethod;
// package of the asked type
String packageName;
// generated class name
String generatedClassName;
// generated class name + package
String fullGeneratedClassName;
@Override
public String generate( TreeLogger logger, GeneratorContext context, String typeName ) throws UnableToCompleteException
{
// We have already processed all the class bundles
if(fullGeneratedClassName != null) {
return fullGeneratedClassName;
}
this.logger = logger;
this.context = context;
this.askedTypeName = typeName;
// get the "reflection" machine of GWT compiler
typeOracle = context.getTypeOracle();
try {
// get classType and save instance variables
askedType = typeOracle.getType( typeName );
List<JClassType> clazzBundles = getClazzBundles();
clazzBundles.add(askedType);
introspectedTypes = new HashSet<JType>();
// Ensure only one method exists
if(askedType.getMethods().length > 1) {
logger.log(TreeLogger.Type.WARN, "You should only have 1 method " +
"registering the class information, the first method will be used.");
}
registerMethod = askedType.getMethods()[0];
for(JClassType bundle : clazzBundles) {
// All introspected classes will be combined
if(bundle.getMethods().length > 0) {
JMethod method = bundle.getMethods()[0];
// list all return types of all methods
ReflectedClasses classes = method.getAnnotation(ReflectedClasses.class);
if (classes == null || classes.classes() == null || classes.classes().length == 0)
continue;
for (Class<?> clazz : classes.classes()) {
JType classType = typeOracle.getType(clazz.getName());
if (classType != null)
introspectedTypes.add(classType);
}
}
}
// Generation information
packageName = askedType.getPackage().getName();
generatedClassName = askedType.getSimpleSourceName() + "ClazzBundleImpl";
// Generate class source code
generateClass();
}
catch(Exception e) {
// record to logger that Map generation threw an exception
logger.log( TreeLogger.ERROR, "ERROR when generating " + generatedClassName + " for " + typeName, e );
}
// return the fully qualifed name of the class generated
fullGeneratedClassName = packageName + "." + generatedClassName;
return fullGeneratedClassName;
}
private List<JClassType> getClazzBundles() {
List<JClassType> clazzBundles = new ArrayList<>();
// Generate all other class bundles
JClassType[] types = typeOracle.getTypes();
for(JClassType type : types) {
if(TypeHelper.isInstanceOf(type, ClazzBundle.class)) {
clazzBundles.add(type);
}
}
return clazzBundles;
}
private void generateClass()
{
// 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.addImplementedInterface( askedType.getParameterizedQualifiedSourceName() );
composer.addImport( "fr.lteconsulting.hexa.classinfo.Clazz" );
composer.addImport( "fr.lteconsulting.hexa.classinfo.ClassInfo" );
composer.addImport( "com.google.gwt.core.shared.GWT" );
SourceWriter sourceWriter = composer.createSourceWriter( context, printWriter );
// generate the List<String> getMethods(); method
generateClass( sourceWriter );
// close generated class
sourceWriter.outdent();
sourceWriter.println( "}" );
// commit generated class
context.commit( logger, printWriter );
}
private void generateClass( SourceWriter sourceWriter )
{
sourceWriter.println( "" );
for( JType type : introspectedTypes )
{
String interfaceName = "Clazz_" + type.getQualifiedSourceName().replaceAll( "\\.", "_" );
sourceWriter.println( "public interface " + interfaceName + " extends Clazz<" + type.getQualifiedSourceName() + "> {}" );
}
sourceWriter.println( "" );
sourceWriter.println( "public void "+registerMethod.getName()+"()" );
sourceWriter.println( "{" );
sourceWriter.indent();
for(JType type : introspectedTypes) {
String interfaceName = "Clazz_" + type.getQualifiedSourceName().replaceAll( "\\.", "_" );
sourceWriter.println("ClassInfo.RegisterClazz( (Clazz<?>) GWT.create( " + interfaceName + ".class ) );" );
}
sourceWriter.outdent();
sourceWriter.println( "}" );
sourceWriter.println( "" );
}
}