added new options for APT class generation

This commit is contained in:
Timo Westkämper 2008-11-16 14:03:48 +00:00
parent 396e49a681
commit 151f5c248d
9 changed files with 282 additions and 143 deletions

View File

@ -1,43 +0,0 @@
/*
* Copyright (c) 2008 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* FreeMarkerSerializer provides.
*
* @author tiwe
* @version $Id$
*/
public final class FreeMarkerSerializer {
private final Configuration cfg;
private final String templateLocation;
public FreeMarkerSerializer(String template){
if (template == null) throw new IllegalArgumentException("template was null");
cfg = new Configuration();
cfg.setClassForTemplateLoading(this.getClass(), "/");
cfg.setObjectWrapper(new DefaultObjectWrapper());
templateLocation = template;
}
public void serialize(Map<String,Object> model, Writer writer) throws IOException, TemplateException{
Template template = cfg.getTemplate(templateLocation);
template.process(model, writer);
writer.flush();
}
}

View File

@ -0,0 +1,50 @@
package com.mysema.query.apt;
import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.TemplateException;
/**
* Serializer provides
*
* @author tiwe
* @version $Id$
*/
public interface Serializer {
/**
*
* @param model
* @param writer
* @throws Exception
*/
public void serialize(Map<String,Object> model, Writer writer) throws Exception;
public final class FreeMarker implements Serializer{
private static final Configuration cfg;
static {
cfg = new Configuration();
cfg.setClassForTemplateLoading(Serializer.class, "/");
cfg.setObjectWrapper(new DefaultObjectWrapper());
}
private final String templateLocation;
public FreeMarker(String template){
if (template == null) throw new IllegalArgumentException("template was null");
templateLocation = template;
}
public void serialize(Map<String,Object> model, Writer writer) throws IOException, TemplateException{
cfg.getTemplate(templateLocation).process(model, writer);
writer.flush(); }
}
}

View File

@ -8,18 +8,15 @@ package com.mysema.query.apt.hibernate;
import static com.sun.mirror.util.DeclarationVisitors.NO_OP;
import static com.sun.mirror.util.DeclarationVisitors.getDeclarationScanner;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.io.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
import org.apache.commons.io.FileUtils;
import com.mysema.query.apt.FreeMarkerSerializer;
import com.mysema.query.apt.Serializer;
import com.mysema.query.apt.Type;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
@ -33,20 +30,26 @@ import com.sun.mirror.declaration.Declaration;
* @version $Id$
*/
public class HibernateProcessor implements AnnotationProcessor {
static final Serializer
DOMAIN_INNER_TMPL = new Serializer.FreeMarker("/domain-as-inner-classes.ftl"),
DOMAIN_OUTER_TMPL = new Serializer.FreeMarker("/domain-as-outer-classes.ftl"),
DTO_INNER_TMPL = new Serializer.FreeMarker("/dto-as-inner-classes.ftl"),
DTO_OUTER_TMPL = new Serializer.FreeMarker("/dto-as-outer-classes.ftl");
private final String destClass, destPackage, dtoClass, dtoPackage;
private final String include, namePrefix, targetFolder;
private final AnnotationProcessorEnvironment env;
private final String include, namePrefix, targetFolder;
public HibernateProcessor(AnnotationProcessorEnvironment env) throws IOException {
this.env = env;
this.targetFolder = env.getOptions().get("-s");
this.destClass = getString(env.getOptions(), "-AdestClass=", "");
this.destPackage = getString(env.getOptions(), "-AdestPackage=", "");
this.dtoClass = getString(env.getOptions(), "-AdtoClass=", "");
this.dtoPackage = getString(env.getOptions(), "-AdtoPackage=", "");
this.destClass = getString(env.getOptions(), "-AdestClass=", null);
this.destPackage = getString(env.getOptions(), "-AdestPackage=", null);
this.dtoClass = getString(env.getOptions(), "-AdtoClass=", null);
this.dtoPackage = getString(env.getOptions(), "-AdtoPackage=", null);
this.include = getFileContent(env.getOptions(), "-Ainclude=", "");
this.namePrefix = getString(env.getOptions(), "-AnamePrefix=", "");
}
@ -84,8 +87,7 @@ public class HibernateProcessor implements AnnotationProcessor {
// domain types
visitor1 = new EntityVisitor();
a = (AnnotationTypeDeclaration) env
.getTypeDeclaration("javax.persistence.Entity");
a = (AnnotationTypeDeclaration) env.getTypeDeclaration("javax.persistence.Entity");
for (Declaration typeDecl : env.getDeclarationsAnnotatedWith(a)) {
typeDecl.accept(getDeclarationScanner(visitor1, NO_OP));
}
@ -95,21 +97,13 @@ public class HibernateProcessor implements AnnotationProcessor {
addSupertypeFields(typeDecl, entityTypes, mappedSupertypes);
}
// populate model
Map<String, Object> model = new HashMap<String, Object>();
model.put("domainTypes", new TreeSet<Type>(entityTypes.values()));
model.put("pre", namePrefix);
model.put("include", include);
model.put("package", destClass.substring(0, destClass.lastIndexOf('.')));
model.put("classSimpleName", destClass.substring(destClass.lastIndexOf('.') + 1));
// serialize it
String path = destClass.replace('.', '/') + ".java";
File file = new File(targetFolder, path);
if (!file.getParentFile().mkdirs()){
System.err.println("Folder " + file.getParent() + " could not be created");
if (destClass != null){
serializeAsInnerClasses(entityTypes.values());
}else if (destPackage != null){
serializeAsOuterClasses(entityTypes.values());
}else{
System.err.print("No class generation for domain types");
}
serialize(file, "/querydsl-hibernate-domain.ftl", model);
}
@ -121,22 +115,15 @@ public class HibernateProcessor implements AnnotationProcessor {
typeDecl.accept(getDeclarationScanner(visitor2, NO_OP));
}
// populate model
Map<String, Object> model = new HashMap<String, Object>();
model.put("dtoTypes", visitor2.types);
model.put("pre", namePrefix);
model.put("package", dtoClass.substring(0, dtoClass.lastIndexOf('.')));
model.put("classSimpleName", dtoClass.substring(dtoClass.lastIndexOf('.') + 1));
// serialize it
String path = dtoClass.replace('.', '/') + ".java";
File file = new File(targetFolder, path);
if (!file.getParentFile().mkdirs()){
System.err.println("Folder " + file.getParent() + " could not be created");
if (dtoClass != null){
serializeDTOsAsInnerClasses(visitor2.types);
}else if (dtoPackage != null){
serializeDTOsAsOuterClasses(visitor2.types);
}else{
System.err.print("No class generation for DTO types");
}
serialize(file, "/querydsl-hibernate-dto.ftl", model);
}
private String getFileContent(Map<String, String> options, String prefix,
String defaultValue) throws IOException {
for (Map.Entry<String, String> entry : options.entrySet()) {
@ -147,7 +134,8 @@ public class HibernateProcessor implements AnnotationProcessor {
}
return defaultValue;
}
private String getString(Map<String, String> options, String prefix,
String defaultValue) {
for (Map.Entry<String, String> entry : options.entrySet()) {
@ -163,14 +151,108 @@ public class HibernateProcessor implements AnnotationProcessor {
if (!"".equals(dtoClass)) createDTOClasses();
}
private void serialize(File file, String template, Map<String,Object> model){
private void serializeAsInnerClasses(Collection<Type> entityTypes) {
// populate model
Map<String, Object> model = new HashMap<String, Object>();
model.put("domainTypes", new TreeSet<Type>(entityTypes));
model.put("pre", namePrefix);
model.put("include", include);
model.put("package", destClass.substring(0, destClass.lastIndexOf('.')));
model.put("classSimpleName", destClass.substring(destClass.lastIndexOf('.') + 1));
// serialize it
String path = destClass.replace('.', '/') + ".java";
File file = new File(targetFolder, path);
if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()){
System.err.println("Folder " + file.getParent() + " could not be created");
}
try {
Writer out = new OutputStreamWriter(new FileOutputStream(file));
new FreeMarkerSerializer(template).serialize(model, out);
} catch (Exception e) {
DOMAIN_INNER_TMPL.serialize(model, writerFor(file));
} catch (Exception e) {
throw new RuntimeException("Caught exception",e);
}
}
private void serializeAsOuterClasses(Collection<Type> entityTypes) {
// populate model
Map<String, Object> model = new HashMap<String, Object>();
model.put("pre", namePrefix);
model.put("include", include);
model.put("package", destPackage);
for (Type type : entityTypes){
model.put("type", type);
model.put("classSimpleName", type.getSimpleName());
// serialize it
String path = destPackage.replace('.', '/') + "/" + namePrefix + type.getSimpleName() + ".java";
File file = new File(targetFolder, path);
if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()){
System.err.println("Folder " + file.getParent() + " could not be created");
}
try {
DOMAIN_OUTER_TMPL.serialize(model, writerFor(file));
} catch (Exception e) {
throw new RuntimeException("Caught exception",e);
}
}
}
private void serializeDTOsAsInnerClasses(Collection<Type> types) {
// populate model
Map<String, Object> model = new HashMap<String, Object>();
model.put("dtoTypes", types);
model.put("pre", namePrefix);
model.put("package", dtoClass.substring(0, dtoClass.lastIndexOf('.')));
model.put("classSimpleName", dtoClass.substring(dtoClass.lastIndexOf('.') + 1));
// serialize it
String path = dtoClass.replace('.', '/') + ".java";
File file = new File(targetFolder, path);
if (!file.getParentFile().mkdirs()){
System.err.println("Folder " + file.getParent() + " could not be created");
}
try {
DTO_INNER_TMPL.serialize(model, writerFor(file));
} catch (Exception e) {
throw new RuntimeException("Caught exception",e);
}
}
private void serializeDTOsAsOuterClasses(Collection<Type> types){
// populate model
Map<String, Object> model = new HashMap<String, Object>();
model.put("pre", namePrefix);
model.put("include", include);
model.put("package", dtoPackage);
for (Type type : types){
model.put("type", type);
model.put("classSimpleName", type.getSimpleName());
// serialize it
String path = dtoPackage.replace('.', '/') + "/" + namePrefix + type.getSimpleName() + ".java";
File file = new File(targetFolder, path);
if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()){
System.err.println("Folder " + file.getParent() + " could not be created");
}
try {
DTO_OUTER_TMPL.serialize(model, writerFor(file));
} catch (Exception e) {
throw new RuntimeException("Caught exception",e);
}
}
}
private Writer writerFor(File file){
try {
return new OutputStreamWriter(new FileOutputStream(file));
} catch (FileNotFoundException e) {
String error = "Caught " + e.getClass().getName();
throw new RuntimeException(error, e);
}
}
}

View File

@ -0,0 +1,20 @@
<#import "/macros.ftl" as cl>
package ${package};
import com.mysema.query.grammar.types.*;
import static com.mysema.query.grammar.types.PathMetadata.*;
/**
* ${classSimpleName} provides types for use in Query DSL constructs
*
*/
public class ${classSimpleName} {
${include}
<#list domainTypes as type>
public static final class ${pre}${type.simpleName} extends Path.Entity<${type.name}>{
<@cl.classContent decl=type/>
}
</#list>
}

View File

@ -0,0 +1,13 @@
<#import "/macros.ftl" as cl>
package ${package};
import com.mysema.query.grammar.types.*;
import static com.mysema.query.grammar.types.PathMetadata.*;
/**
* ${classSimpleName} provides types for use in Query DSL constructs
*
*/
public class ${pre}${classSimpleName} extends Path.Entity<${type.name}>{
<@cl.classContent decl=type/>
}

View File

@ -0,0 +1,17 @@
<#import "/macros.ftl" as cl>
package ${package};
import static com.mysema.query.grammar.types.HqlTypes.*;
import com.mysema.query.grammar.types.*;
/**
* ${classSimpleName} provides types for use in Query DSL constructs
*
*/
public class ${pre}${classSimpleName} extends Constructor<${type.name}>{
<#list type.constructors as co>
public ${pre}${type.simpleName}(<#list co.parameters as pa>Expr<${pa.typeName}> ${pa.name}<#if pa_has_next>,</#if></#list>){
super(${type.name}.class<#list co.parameters as pa>,${pa.name}</#list>);
}
</#list>
}

View File

@ -1,26 +1,13 @@
<#assign reserved = ["isnull", "isnotnull", "getType", "getMetadata", "toString", "hashCode", "getClass", "notify", "notifyAll", "wait"]>
package ${package};
import com.mysema.query.grammar.types.*;
import static com.mysema.query.grammar.types.PathMetadata.*;
/**
* ${classSimpleName} provides types for use in Query DSL constructs
*
*/
public class ${classSimpleName} {
${include}
<#list domainTypes as decl>
public static final class ${pre}${decl.simpleName} extends Path.Entity<${decl.name}>{
<#list decl.stringFields as field>
<#macro classContent decl>
<#assign reserved = ["isnull", "isnotnull", "getType", "getMetadata", "toString", "hashCode", "getClass", "notify", "notifyAll", "wait"]>
<#list decl.stringFields as field>
public final Path.String ${field.name} = _string("${field.name}");
</#list>
<#list decl.booleanFields as field>
public final Path.Boolean ${field.name} = _boolean("${field.name}");
</#list>
<#--
simple fields
-->
<#-- simple fields -->
<#list decl.simpleFields as field>
public final Path.Comparable<${field.typeName}> ${field.name} = _comparable("${field.name}",${field.typeName}.class);
</#list>
@ -45,9 +32,8 @@ ${include}
return new Path.Simple<${field.typeName}>(${field.typeName}.class,forListAccess(${field.name},index));
}
</#list>
<#--
entity fields
-->
<#-- entity fields -->
<#list decl.entityMaps as field>
public final Path.EntityMap<${field.keyTypeName},${field.typeName}> ${field.name} = _entitymap("${field.name}",${field.keyTypeName}.class,${field.typeName}.class);
public ${pre}${field.simpleTypeName} ${field.name}(${field.keyTypeName} key) {
@ -78,9 +64,8 @@ ${include}
}
</#if>
</#list>
<#--
constructors
-->
<#-- constructors -->
public ${pre}${decl.simpleName}(java.lang.String path) {
super(${decl.name}.class, path);
<#list decl.entityFields as field>
@ -92,8 +77,4 @@ ${include}
public ${pre}${decl.simpleName}(PathMetadata<?> metadata) {
super(${decl.name}.class, metadata);
}
}
</#list>
}
</#macro>

View File

@ -5,17 +5,18 @@
*/
package com.mysema.query.apt.hibernate;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import com.mysema.query.apt.*;
import freemarker.template.TemplateException;
import com.mysema.query.apt.Constructor;
import com.mysema.query.apt.Field;
import com.mysema.query.apt.Parameter;
import com.mysema.query.apt.Type;
/**
* HibernateProcessorTest provides.
@ -24,36 +25,54 @@ import freemarker.template.TemplateException;
* @version $Id$
*/
public class HibernateProcessorTest {
private Type type;
private Writer writer = new StringWriter();
private Map<String, Object> model = new HashMap<String, Object>();
public HibernateProcessorTest(){
type = new Type("com.mysema.query.DomainSuperClass","com.mysema.query.DomainClass","DomainClass");
Field field = new Field("field",null,"java.lang.String","java.lang.String",Field.Type.STRING);
type.addField(field);
Parameter param = new Parameter("name","java.lang.String");
type.addConstructor( new Constructor(Collections.singleton(param)));
}
@Test
public void testDomainTypes() throws IOException, TemplateException{
Map<String, Object> model = new HashMap<String, Object>();
model.put("domainTypes", Collections.singleton(createTypeDecl()));
public void testDomainTypesAsInnerClass() throws Exception{
model.put("domainTypes", Collections.singleton(type));
model.put("pre", "");
model.put("include", "");
model.put("package", "com.mysema.query");
model.put("classSimpleName", "Test");
StringWriter writer = new StringWriter();
new FreeMarkerSerializer("/querydsl-hibernate-domain.ftl").serialize(model, writer);
// as inner classes
HibernateProcessor.DOMAIN_INNER_TMPL.serialize(model, writer);
}
@Test
public void testDomainTypesAsOuterClasses() throws Exception{
model.put("type", type);
model.put("pre", "");
model.put("include", "");
model.put("package", "com.mysema.query");
model.put("classSimpleName", "Test");
// as outer classes
HibernateProcessor.DOMAIN_OUTER_TMPL.serialize(model, writer);
}
@Test
public void testDTOTypes() throws IOException, TemplateException{
Map<String, Object> model = new HashMap<String, Object>();
model.put("dtoTypes", Collections.singleton(createTypeDecl()));
public void testDTOTypes() throws Exception{
model.put("dtoTypes", Collections.singleton(type));
model.put("pre", "");
model.put("package", "com.mysema.query");
model.put("classSimpleName", "Test");
StringWriter writer = new StringWriter();
new FreeMarkerSerializer("/querydsl-hibernate-dto.ftl").serialize(model, writer);
}
private Type createTypeDecl() {
Type typeDecl = new Type("com.mysema.query.DomainSuperClass","com.mysema.query.DomainClass","DomainClass");
Field field = new Field("field",null,"java.lang.String","java.lang.String",Field.Type.STRING);
typeDecl.addField(field);
Parameter param = new Parameter("name","java.lang.String");
typeDecl.addConstructor( new Constructor(Collections.singleton(param)));
return typeDecl;
// as inner classes
HibernateProcessor.DTO_INNER_TMPL.serialize(model, writer);
}
}