package fr.lteconsulting.hexa.persistence.rebind;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
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.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import fr.lteconsulting.hexa.persistence.client.legacy.persistence.WithEntityClasses;
public class PersistenceConfigurationFactoryGenerator extends Generator
{
private static final String CLASSBUNDLE_INTERFACE_NAME = "ClassBundle";
// Context and logger for code generation
TreeLogger logger = null;
GeneratorContext context = null;
TypeOracle typeOracle = null;
Class<?>[] entityClasses;
JClassType askedType;
String generatedClassPackageName;
String generatedClassName;
@Override
public String generate( TreeLogger logger, GeneratorContext context, String typeName ) throws UnableToCompleteException
{
this.logger = logger;
this.context = context;
this.typeOracle = context.getTypeOracle();
try
{
// get classType and save instance variables
askedType = typeOracle.getType( typeName );
WithEntityClasses withEntityClassesAnnotation = askedType.getAnnotation( WithEntityClasses.class );
entityClasses = withEntityClassesAnnotation.classes();
generatedClassPackageName = askedType.getPackage().getName();
generatedClassName = askedType.getSimpleSourceName() + "_EntityConfigurationFactoryImpl";
// 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
return generatedClassPackageName + "." + generatedClassName;
}
void generateClass()
{
// get print writer that receives the source code
PrintWriter printWriter = null;
printWriter = context.tryCreate( logger, generatedClassPackageName, 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( generatedClassPackageName, generatedClassName );
composer.addImport( "fr.lteconsulting.hexa.client.classinfo.ReflectedClasses" );
composer.addImport( "fr.lteconsulting.hexa.client.classinfo.ClazzBundle" );
composer.addImport( "fr.lteconsulting.hexa.persistence.client.legacy.persistence.PersistenceConfiguration" );
composer.addImport( "fr.lteconsulting.hexa.persistence.client.legacy.persistence.PersistenceConfigurationFactory" );
composer.addImport( "fr.lteconsulting.hexa.persistence.client.legacy.persistence.PersistenceConfiguration.EntityConfiguration" );
composer.addImport( "javax.persistence.GenerationType" );
composer.addImport( "javax.persistence.FetchType" );
composer.addImport( "com.google.gwt.core.shared.GWT" );
composer.addImplementedInterface( askedType.getParameterizedQualifiedSourceName() );
SourceWriter sourceWriter = composer.createSourceWriter( context, printWriter );
// create the classbundle interface
writeClassBundleInterface( sourceWriter, CLASSBUNDLE_INTERFACE_NAME );
// write the body
writeBody( sourceWriter );
// close generated class
sourceWriter.outdent();
sourceWriter.println( "}" );
// commit generated class
context.commit( logger, printWriter );
}
private Field getIdField( Class<?> clazz )
{
Field[] fields = clazz.getDeclaredFields();
for( int i=0; i<fields.length; i++ )
{
Id idAnnotation = fields[i].getAnnotation( Id.class );
if( idAnnotation != null )
return fields[i];
}
return null;
}
private void writeBody( SourceWriter sourceWriter )
{
sourceWriter.println( "private PersistenceConfiguration config;" );
sourceWriter.println( "@Override" );
sourceWriter.println( "public PersistenceConfiguration getPersistenceConfiguration() {" );
sourceWriter.println( "if( config == null )" );
sourceWriter.println( "createConfig();" );
sourceWriter.println( "return config;" );
sourceWriter.println( "}" );
sourceWriter.println( "private void createConfig() {" );
sourceWriter.println( "ClassBundle clazzBundle = GWT.create( ClassBundle.class );" );
sourceWriter.println( "clazzBundle.register();" );
sourceWriter.println( "config = new PersistenceConfiguration();" );
sourceWriter.println( "EntityConfiguration ec = null;" );
for( int i=0; i<entityClasses.length; i++ )
{
Class<?> entityClass = entityClasses[i];
// ensure class has the @Entity annotation...
assert entityClass.getAnnotation( Entity.class ) != null : "Class xxx should be annotated with @Entity !";
// search @Id field : class, name, generation policy
Field idField = getIdField( entityClass );
assert idField != null : "Entity class should have an @Id field";
String generationTypeString = "GenerationType.SEQUENCE";
GeneratedValue generatedValueAnnotation = idField.getAnnotation( GeneratedValue.class );
if( generatedValueAnnotation != null && generatedValueAnnotation.strategy() != null )
generationTypeString = "GenerationType." + generatedValueAnnotation.strategy().name();
sourceWriter.println( "ec = config.addEntityConfiguration( "+entityClass.getName()+".class, "+idField.getType().getName()+".class, \""+idField.getName()+"\", "+generationTypeString+" );" );
Field[] fields = entityClass.getDeclaredFields();
for( int f=0; f<fields.length; f++ )
{
Id idAnnotation = fields[f].getAnnotation( Id.class );
if( idAnnotation != null )
continue;
// TODO : check transcient, final, ...
OneToMany oneToMany = fields[f].getAnnotation( OneToMany.class );
if( oneToMany != null )
{
//@OneToMany( mappedBy = "category" )
//private List<Article> articles;
String mappedBy = oneToMany.mappedBy();
//ec.addOneToManyFieldConfiguration( List.class, Article.class, "articles", "category", false );
JClassType entityType = typeOracle.findType( entityClass.getName() );
JField jField = entityType.findField( fields[f].getName() );
String containerClassName = jField.getType().getErasedType().getQualifiedSourceName();
String targetClassName = jField.getType().isParameterized().getTypeArgs()[0].getQualifiedSourceName();
sourceWriter.println( "ec.addOneToManyFieldConfiguration( "+containerClassName+".class, "+targetClassName+".class, \""+fields[f].getName()+"\", \""+mappedBy+"\", false );" );
continue;
}
ManyToOne manyToOne = fields[f].getAnnotation( ManyToOne.class );
if( manyToOne != null )
{
//@ManyToOne( fetch = FetchType.LAZY, cascade = { CascadeType.MERGE } )
//@JoinColumn( name="category_id" )
//Category category;
String columnName = fields[f].getName() + "_id";
JoinColumn joinColumn = fields[f].getAnnotation( JoinColumn.class );
if( joinColumn != null )
columnName = joinColumn.name();
String fetchTypeString = "FetchType." + manyToOne.fetch();
// ec.addManyToOneFieldConfiguration( Category.class, "category", "category_id", FetchType.LAZY );
sourceWriter.println( "ec.addManyToOneFieldConfiguration( "+fields[f].getType().getName()+".class, \""+fields[f].getName()+"\", \""+columnName+"\", "+fetchTypeString+" );" );
continue;
}
// normal field fields
sourceWriter.println( "ec.addFieldConfiguration( "+fields[f].getType().getName()+".class, \""+fields[f].getName()+"\" );" );
}
}
sourceWriter.println( "}" );
}
private void writeClassBundleInterface( SourceWriter sourceWriter, String classBundleInterfaceName )
{
StringBuilder sb = new StringBuilder();
for( int i=0; i<entityClasses.length; i++ )
{
if( i > 0 )
sb.append( ", " );
sb.append( entityClasses[i].getName() );
sb.append( ".class" );
}
sourceWriter.println( "interface "+classBundleInterfaceName+" extends ClazzBundle {" );
sourceWriter.println( "@ReflectedClasses( classes = { "+sb.toString()+" } )" );
sourceWriter.println( "void register();" );
sourceWriter.println( "}" );
}
}