added support for factory methods in Querydsl entity query types

added more javadocs to generated Querydsl query types
This commit is contained in:
Timo Westkämper 2009-10-03 15:12:56 +00:00
parent 7e1054be9a
commit 8fa74c3c2a
18 changed files with 224 additions and 49 deletions

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt;
import java.lang.annotation.Annotation;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt;
import java.lang.annotation.Annotation;
@ -39,7 +44,8 @@ public class Configuration {
}
public boolean isValidConstructor(ExecutableElement constructor) {
return constructor.getModifiers().contains(Modifier.PUBLIC);
return constructor.getModifiers().contains(Modifier.PUBLIC)
&& !constructor.getParameters().isEmpty();
}
public boolean isValidField(VariableElement field) {

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt;
import java.util.ArrayList;

View File

@ -1,6 +1,16 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
@ -17,7 +27,9 @@ import org.apache.commons.lang.StringUtils;
import com.mysema.query.annotations.QueryType;
import com.mysema.query.codegen.ClassModel;
import com.mysema.query.codegen.ConstructorModel;
import com.mysema.query.codegen.FieldModel;
import com.mysema.query.codegen.ParameterModel;
import com.mysema.query.codegen.TypeCategory;
import com.mysema.query.codegen.TypeModel;
@ -51,7 +63,57 @@ public final class EntityElementVisitor extends SimpleElementVisitor6<ClassModel
ClassModel classModel = new ClassModel(namePrefix, sc.getName(), c.getPackageName(), c.getName(), c.getSimpleName());
List<? extends Element> elements = e.getEnclosedElements();
// CONSTRUCTORS
for (ExecutableElement constructor : ElementFilter.constructorsIn(elements)){
if (configuration.isValidConstructor(constructor)){
List<ParameterModel> parameters = new ArrayList<ParameterModel>(constructor.getParameters().size());
for (VariableElement var : constructor.getParameters()){
TypeModel varType = typeFactory.create(var.asType(), elementUtils);
parameters.add(new ParameterModel(var.getSimpleName().toString(), varType.getName()));
}
classModel.addConstructor(new ConstructorModel(parameters));
}
}
VisitorConfig config = configuration.getConfig(e, elements);
Set<String> blockedFields = new HashSet<String>();
Map<String,FieldModel> fields = new HashMap<String,FieldModel>();
Map<String,TypeCategory> types = new HashMap<String,TypeCategory>();
// FIELDS
if (config.isVisitFields()){
for (VariableElement field : ElementFilter.fieldsIn(elements)){
String name = field.getSimpleName().toString();
if (configuration.isValidField(field)){
try{
TypeModel typeModel = typeFactory.create(field.asType(), elementUtils);
if (field.getAnnotation(QueryType.class) != null){
TypeCategory typeCategory = TypeCategory.get(field.getAnnotation(QueryType.class).value());
if (typeCategory == null){
blockedFields.add(name);
continue;
}
typeModel = typeModel.as(typeCategory);
types.put(name, typeCategory);
}
fields.put(name, new FieldModel(classModel, name, typeModel, null));
}catch(IllegalArgumentException ex){
StringBuilder builder = new StringBuilder();
builder.append("Caught exception for field ");
builder.append(c.getName()).append("#").append(field.getSimpleName());
throw new RuntimeException(builder.toString(), ex);
}
}else{
blockedFields.add(name);
}
}
}
// METHODS
if (config.isVisitMethods()){
for (ExecutableElement method : ElementFilter.methodsIn(elements)){
@ -67,13 +129,18 @@ public final class EntityElementVisitor extends SimpleElementVisitor6<ClassModel
try{
TypeModel typeModel = typeFactory.create(method.getReturnType(), elementUtils);
if (method.getAnnotation(QueryType.class) != null){
TypeCategory category = TypeCategory.get(method.getAnnotation(QueryType.class).value());
if (category == null){
TypeCategory typeCategory = TypeCategory.get(method.getAnnotation(QueryType.class).value());
if (typeCategory == null){
blockedFields.add(name);
continue;
}else if (blockedFields.contains(name)){
continue;
}
typeModel = typeModel.as(category);
typeModel = typeModel.as(typeCategory);
}else if (types.containsKey(name)){
typeModel = typeModel.as(types.get(name));
}
classModel.addField(new FieldModel(classModel, name, typeModel, null));
fields.put(name, new FieldModel(classModel, name, typeModel, null));
}catch(IllegalArgumentException ex){
StringBuilder builder = new StringBuilder();
@ -81,35 +148,18 @@ public final class EntityElementVisitor extends SimpleElementVisitor6<ClassModel
builder.append(c.getName()).append("#").append(method.getSimpleName());
throw new RuntimeException(builder.toString(), ex);
}
}
}else{
blockedFields.add(name);
}
}
}
if (config.isVisitFields()){
for (VariableElement field : ElementFilter.fieldsIn(elements)){
if (configuration.isValidField(field)){
try{
TypeModel typeModel = typeFactory.create(field.asType(), elementUtils);
if (field.getAnnotation(QueryType.class) != null){
TypeCategory category = TypeCategory.get(field.getAnnotation(QueryType.class).value());
if (category == null){
continue;
}
typeModel = typeModel.as(category);
}
String name = field.getSimpleName().toString();
classModel.addField(new FieldModel(classModel, name, typeModel, null));
}catch(IllegalArgumentException ex){
StringBuilder builder = new StringBuilder();
builder.append("Caught exception for field ");
builder.append(c.getName()).append("#").append(field.getSimpleName());
throw new RuntimeException(builder.toString(), ex);
}
}
}
}
for (Map.Entry<String,FieldModel> entry : fields.entrySet()){
if (!blockedFields.contains(entry.getKey())){
classModel.addField(entry.getValue());
}
}
return classModel;
}

View File

@ -35,7 +35,6 @@ import com.mysema.query.codegen.TypeModelFactory;
* @author tiwe
*
*/
// TODO : simplify this
@Immutable
public class Processor {

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt;
/**

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt;
import javax.lang.model.type.TypeMirror;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt;
/**
@ -33,5 +38,4 @@ public enum VisitorConfig {
return methods;
}
}

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt.jdo;
import java.lang.annotation.Annotation;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt.jpa;
import java.lang.annotation.Annotation;

View File

@ -1,3 +1,8 @@
/*
* Copyright (c) 2009 Mysema Ltd.
* All rights reserved.
*
*/
package com.mysema.query.apt.jpa;
import java.lang.annotation.Annotation;

View File

@ -30,7 +30,6 @@ public class ColQueryTemplates extends JavaTemplates {
add(Ops.AOE, "{0}.compareTo({1}) >= 0");
add(Ops.BOE, "{0}.compareTo({1}) <= 0");
add(Ops.BETWEEN, functions + ".between({0},{1},{2})");
// add(Ops.BETWEEN, "{0}.compareTo({1}) > 0 && {0}.compareTo({2}) < 0");
add(Ops.STRING_CAST, "String.valueOf({0})");
// Date and Time
@ -60,6 +59,7 @@ public class ColQueryTemplates extends JavaTemplates {
}
public static boolean like(String str, String like){
// TODO : better escaping
return str.matches(like.replace("%", ".*").replace('_', '.'));
}

View File

@ -32,11 +32,9 @@ public class Cat extends Animal {
private Cat mate;
@SuppressWarnings("unused")
@QueryType(PropertyType.NONE)
private String skippedField;
@SuppressWarnings("unused")
@QueryType(PropertyType.SIMPLE)
private String stringAsSimple;
@ -106,9 +104,13 @@ public class Cat extends Animal {
this.mate = mate;
}
// public String getStringAsSimple() {
// return stringAsSimple;
// }
public String getStringAsSimple() {
return stringAsSimple;
}
public String getSkippedField() {
return skippedField;
}
public void setStringAsSimple(String stringAsSimple) {
this.stringAsSimple = stringAsSimple;

View File

@ -5,18 +5,31 @@
*/
package com.mysema.query.codegen;
import java.io.Writer;
public class EmbeddableSerializer extends EntitySerializer{
@Override
protected void introDefaultInstance(StringBuilder builder, ClassModel model) {
// no default instance
}
@Override
protected void constructorsForVariables(StringBuilder builder, ClassModel model) {
// no root constructors
}
@Override
protected void factoryMethods(ClassModel model, Writer writer) {
// no factory methods
}
@Override
protected void introImports(StringBuilder builder) {
builder.append("import com.mysema.query.util.*;\n");
builder.append("import com.mysema.query.types.path.*;\n\n");
}
@Override
protected void introDefaultInstance(StringBuilder builder, ClassModel model) {
// no default instance
}
}

View File

@ -17,6 +17,9 @@ public class EntitySerializer implements Serializer{
// intro
intro(model, writer);
// factory methods
factoryMethods(model, writer);
// fields
for (FieldModel field : model.getStringFields()){
stringField(field, writer);
@ -88,6 +91,38 @@ public class EntitySerializer implements Serializer{
outro(model, writer);
}
protected void factoryMethods(ClassModel model, Writer writer) throws IOException {
final String localName = model.getLocalName();
StringBuilder builder = new StringBuilder();
for (ConstructorModel c : model.getConstructors()){
// begin
builder.append(" /**\n");
builder.append(" * Factory method for projection\n");
builder.append(" */\n");
builder.append(" public static EConstructor<" + localName + "> create(");
boolean first = true;
for (ParameterModel p : c.getParameters()){
if (!first) builder.append(", ");
builder.append("Expr<" + p.getTypeName() + "> " + p.getName());
first = false;
}
builder.append("){\n");
// body
builder.append(" return new EConstructor<" + localName + ">(" + localName + ".class");
for (ParameterModel p : c.getParameters()){
builder.append(", " + p.getName());
}
// end
builder.append(");\n");
builder.append(" }\n\n");
}
writer.append(builder.toString());
}
protected void booleanField(FieldModel field, Writer writer) throws IOException {
serialize(field, "PBoolean", writer, "createBoolean");
}
@ -155,6 +190,9 @@ public class EntitySerializer implements Serializer{
final String escapedName = field.getEscapedName();
final String queryType = field.getQueryTypeName();
StringBuilder builder = new StringBuilder();
builder.append(" /**\n");
builder.append(" * Lazy creation of "+fieldName+" field\n");
builder.append(" */\n");
builder.append(" public " + queryType + " _" + fieldName + "() {\n");
builder.append(" if (" + escapedName + " == null){\n");
builder.append(" " + escapedName + " = new " + queryType + "(PathMetadata.forProperty(this,\"" + fieldName + "\"));\n");
@ -180,6 +218,9 @@ public class EntitySerializer implements Serializer{
ClassModel _super = model.getSuperModel();
final String simpleName = _super.getSimpleName();
final String queryType = _super.getPrefix() + simpleName;
builder.append(" /**\n");
builder.append(" * Reference to the same path with the supertype signature\n");
builder.append(" */\n");
builder.append(" public final "+queryType+" _super = new " + queryType + "(this);\n\n");
}
}
@ -200,7 +241,8 @@ public class EntitySerializer implements Serializer{
protected void introImports(StringBuilder builder) {
builder.append("import com.mysema.query.util.*;\n");
builder.append("import com.mysema.query.types.path.*;\n\n");
builder.append("import com.mysema.query.types.path.*;\n");
builder.append("import com.mysema.query.types.expr.*;\n");
}
protected void introJavadoc(StringBuilder builder, ClassModel model) {

View File

@ -22,6 +22,17 @@ public class SupertypeSerializer extends EntitySerializer{
// no default instance
}
@Override
protected void factoryMethods(ClassModel model, Writer writer) {
// no factory methods
}
@Override
protected void introImports(StringBuilder builder) {
builder.append("import com.mysema.query.util.*;\n");
builder.append("import com.mysema.query.types.path.*;\n\n");
}
@Override
protected void constructors(ClassModel model, Writer writer) throws IOException {
final String simpleName = model.getSimpleName();

View File

@ -15,6 +15,7 @@ import java.util.regex.Pattern;
import net.jcip.annotations.Immutable;
import com.mysema.query.types.Template.Element;
import com.mysema.query.types.expr.Constant;
import com.mysema.query.types.expr.EString;
/**
@ -43,26 +44,38 @@ public class TemplateFactory {
private final Converter<EString,EString> toStartsWithViaLike = new Converter<EString,EString>(){
@Override
public EString convert(EString arg) {
return arg.append("%");
return escapeForLike(arg).append("%");
}
};
private final Converter<EString,EString> toEndsWithViaLike = new Converter<EString,EString>(){
@Override
public EString convert(EString arg) {
return arg.prepend("%");
return escapeForLike(arg).prepend("%");
}
};
private final Converter<EString,EString> toContainsViaLike = new Converter<EString,EString>(){
@Override
public EString convert(EString arg) {
return arg.prepend("%").append("%");
return escapeForLike(arg).prepend("%").append("%");
}
};
private final Map<String,Template> cache = new HashMap<String,Template>();
@SuppressWarnings("unchecked")
private EString escapeForLike(EString expr){
if (expr instanceof Constant){
String str = ((Constant<String>) expr).getConstant();
if (str.contains("%") || str.contains("_")){
str = str.replace("%", "\\%").replace("_", "\\_");
return EString.create(str);
}
}
return expr;
}
public Template create(String template){
if (cache.containsKey(template)){
return cache.get(template);

View File

@ -59,7 +59,7 @@ public class HQLTemplates extends Templates {
// string
add(Ops.CONCAT, "{0} || {1}", 37);
add(Ops.MATCHES, "{0} like {1}", 27); // TODO : as real regex
add(Ops.MATCHES, "{0} like {1}", 27); // TODO : support real regexes
add(Ops.LOWER, "lower({0})");
add(Ops.SUBSTR_1ARG, "substring({0},{1})");
add(Ops.SUBSTR_2ARGS, "substring({0},{1},{2})");