From 453a2e8dbb593502e057185beaa3adb91615dd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Westk=C3=A4mper?= Date: Tue, 20 Oct 2009 09:10:46 +0000 Subject: [PATCH] added improved support for custom literal types added improved support for interface based entity types --- .../com/mysema/query/apt/APTModelFactory.java | 43 ++++++-- .../com/mysema/query/apt/Configuration.java | 10 ++ .../mysema/query/apt/DTOElementVisitor.java | 7 +- .../query/apt/EntityElementVisitor.java | 9 +- .../java/com/mysema/query/apt/Processor.java | 49 +++++---- .../com/mysema/query/domain/EntityTest.java | 2 +- .../query/domain/InterfaceTypeTest.java | 55 +++++++++-- .../com/mysema/query/domain/RelationTest.java | 2 +- .../query/domain/ReservedNamesTest.java | 14 ++- .../mysema/query/domain/SimpleTypesTest.java | 99 ++++++++++++------- .../com/mysema/query/codegen/BeanModel.java | 2 - .../query/codegen/TypeModelFactory.java | 3 + 12 files changed, 203 insertions(+), 92 deletions(-) diff --git a/querydsl-apt/src/main/java/com/mysema/query/apt/APTModelFactory.java b/querydsl-apt/src/main/java/com/mysema/query/apt/APTModelFactory.java index 7da34bfa6..9098675f2 100644 --- a/querydsl-apt/src/main/java/com/mysema/query/apt/APTModelFactory.java +++ b/querydsl-apt/src/main/java/com/mysema/query/apt/APTModelFactory.java @@ -12,6 +12,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; @@ -39,6 +40,8 @@ import com.mysema.query.util.TypeUtil; */ public class APTModelFactory implements TypeVisitor { + private final ProcessingEnvironment env; + private final TypeModelFactory factory; private final TypeModel defaultValue; @@ -47,10 +50,15 @@ public class APTModelFactory implements TypeVisitor { private final List> entityAnnotations; - public APTModelFactory(TypeModelFactory factory, List> annotations){ + private final TypeElement numberType, comparableType; + + public APTModelFactory(ProcessingEnvironment env, TypeModelFactory factory, List> annotations){ + this.env = env; this.factory = factory; this.defaultValue = factory.create(Object.class); this.entityAnnotations = annotations; + this.numberType = env.getElementUtils().getTypeElement(Number.class.getName()); + this.comparableType = env.getElementUtils().getTypeElement(Comparable.class.getName()); } public TypeModel create(TypeMirror type, Elements el){ @@ -95,6 +103,13 @@ public class APTModelFactory implements TypeVisitor { } private TypeModel createInterfaceType(DeclaredType t, TypeElement typeElement, Elements p) { + // entity type + for (Class entityAnn : entityAnnotations){ + if (typeElement.getAnnotation(entityAnn) != null){ + return create(typeElement, TypeCategory.ENTITY, p); + } + } + String name = typeElement.getQualifiedName().toString(); String simpleName = typeElement.getSimpleName().toString(); Iterator i = t.getTypeArguments().iterator(); @@ -136,16 +151,30 @@ public class APTModelFactory implements TypeVisitor { // other String name = typeElement.getQualifiedName().toString(); TypeCategory typeCategory = TypeCategory.get(name); - if (!typeCategory.isSubCategoryOf(TypeCategory.COMPARABLE)){ - for(TypeMirror iface : typeElement.getInterfaces()){ - if (iface.toString().contains("java.lang.Comparable")){ - typeCategory = TypeCategory.COMPARABLE; - } - } + + if (typeCategory != TypeCategory.NUMERIC + && isAssignable(typeElement, comparableType) + && isSubType(typeElement, numberType)){ + typeCategory = TypeCategory.NUMERIC; + + }else if (!typeCategory.isSubCategoryOf(TypeCategory.COMPARABLE) + && isAssignable(typeElement, comparableType)){ + typeCategory = TypeCategory.COMPARABLE; } return create(typeElement, typeCategory, p); } + private boolean isSubType(TypeElement type1, TypeElement type2) { + return env.getTypeUtils().isSubtype(type1.asType(), type2.asType()); + } + + private boolean isAssignable(TypeElement type1, TypeElement type2) { + TypeMirror t1 = type1.asType(); + TypeMirror t2 = env.getTypeUtils().erasure(type2.asType()); + return env.getTypeUtils().isAssignable(t1, t2); + } + + private TypeModel create(TypeElement typeElement, TypeCategory category, Elements p) { String name = typeElement.getQualifiedName().toString(); String simpleName = typeElement.getSimpleName().toString(); diff --git a/querydsl-apt/src/main/java/com/mysema/query/apt/Configuration.java b/querydsl-apt/src/main/java/com/mysema/query/apt/Configuration.java index 601e3d1c2..743b26af1 100644 --- a/querydsl-apt/src/main/java/com/mysema/query/apt/Configuration.java +++ b/querydsl-apt/src/main/java/com/mysema/query/apt/Configuration.java @@ -22,6 +22,8 @@ import com.mysema.commons.lang.Assert; */ public class Configuration { + private String namePrefix = "Q"; + protected final Class entityAnn, superTypeAnn, embeddableAnn, dtoAnn, skipAnn; private boolean useFields = true, useGetters = true; @@ -97,4 +99,12 @@ public class Configuration { this.useFields = b; } + public String getNamePrefix() { + return namePrefix; + } + + public void setNamePrefix(String namePrefix) { + this.namePrefix = namePrefix; + } + } diff --git a/querydsl-apt/src/main/java/com/mysema/query/apt/DTOElementVisitor.java b/querydsl-apt/src/main/java/com/mysema/query/apt/DTOElementVisitor.java index de1ea3677..e4d7f5b26 100644 --- a/querydsl-apt/src/main/java/com/mysema/query/apt/DTOElementVisitor.java +++ b/querydsl-apt/src/main/java/com/mysema/query/apt/DTOElementVisitor.java @@ -34,16 +34,13 @@ public final class DTOElementVisitor extends SimpleElementVisitor6emptySet()); List elements = e.getEnclosedElements(); diff --git a/querydsl-apt/src/main/java/com/mysema/query/apt/EntityElementVisitor.java b/querydsl-apt/src/main/java/com/mysema/query/apt/EntityElementVisitor.java index 0c988fc00..2dd0f814a 100644 --- a/querydsl-apt/src/main/java/com/mysema/query/apt/EntityElementVisitor.java +++ b/querydsl-apt/src/main/java/com/mysema/query/apt/EntityElementVisitor.java @@ -37,8 +37,6 @@ import com.mysema.query.codegen.PropertyModel; import com.mysema.query.codegen.TypeCategory; import com.mysema.query.codegen.TypeModel; -import javax.lang.model.type.TypeMirror; - /** * @author tiwe * @@ -48,16 +46,13 @@ public final class EntityElementVisitor extends SimpleElementVisitor6 elements = e.getEnclosedElements(); diff --git a/querydsl-apt/src/main/java/com/mysema/query/apt/Processor.java b/querydsl-apt/src/main/java/com/mysema/query/apt/Processor.java index 13fd1938c..ab773bdfd 100644 --- a/querydsl-apt/src/main/java/com/mysema/query/apt/Processor.java +++ b/querydsl-apt/src/main/java/com/mysema/query/apt/Processor.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Stack; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; @@ -41,8 +42,6 @@ public class Processor { private final APTModelFactory typeFactory; - private final String namePrefix = "Q"; - private final Configuration conf; private final BeanModelFactory classModelFactory; @@ -58,7 +57,7 @@ public class Processor { } this.env = Assert.notNull(env); TypeModelFactory factory = new TypeModelFactory(anns); - this.typeFactory = new APTModelFactory(factory, anns); + this.typeFactory = new APTModelFactory(env, factory, anns); if (conf.getSkipAnn() != null){ this.classModelFactory = new BeanModelFactory(factory, conf.getSkipAnn()); }else{ @@ -71,7 +70,7 @@ public class Processor { public void process(RoundEnvironment roundEnv) { Map superTypes = new HashMap(); - EntityElementVisitor entityVisitor = new EntityElementVisitor(env, conf, namePrefix, typeFactory); + EntityElementVisitor entityVisitor = new EntityElementVisitor(env, conf, typeFactory); // populate super type mappings if (conf.getSuperTypeAnn() != null) { @@ -131,7 +130,7 @@ public class Processor { // DTOS (optional) if (conf.getDtoAnn() != null){ - DTOElementVisitor dtoVisitor = new DTOElementVisitor(env, conf, namePrefix, typeFactory); + DTOElementVisitor dtoVisitor = new DTOElementVisitor(env, conf, typeFactory); Map dtos = new HashMap(); for (Element element : roundEnv.getElementsAnnotatedWith(conf.getDtoAnn())) { BeanModel model = element.accept(dtoVisitor, null); @@ -151,31 +150,29 @@ public class Processor { // iterate over supertypes for (Map stypes : superTypes){ if (stypes.containsKey(stype)) { - while (true) { - BeanModel sdecl; - if (stypes.containsKey(stype)) { - sdecl = stypes.get(stype); - } else { - break; + Stack stypeStack = new Stack(); + stypeStack.push(stype); + while (!stypeStack.isEmpty()){ + String top = stypeStack.pop(); + if (stypes.containsKey(top)){ + BeanModel sdecl = stypes.get(top); + if (singleSuperType && model.getSuperTypes().contains(top)){ + model.setSuperModel(sdecl); + } + if (sdecl.isEntityModel()){ + model.include(sdecl); + } + for (String type : sdecl.getSuperTypes()){ + stypeStack.push(type); + } } - if (singleSuperType && model.getSuperTypes().contains(stype)){ - model.setSuperModel(sdecl); - } - if (sdecl.isEntityModel()){ - model.include(sdecl); - } - if (sdecl.getSuperTypes().isEmpty()){ - stype = null; - }else{ - stype = sdecl.getSuperTypes().iterator().next(); - } - } + } } } } // create super class model via reflection - if (model.getSuperModel() == null && model.getSuperTypes().size() == 1){ + if (model.getSuperModel() == null && singleSuperType){ String stype = model.getSuperTypes().iterator().next(); Class superClass = safeClassForName(stype); if (superClass != null && !superClass.equals(Object.class)) { @@ -183,7 +180,7 @@ public class Processor { if((conf.getSuperTypeAnn() == null || superClass.getAnnotation(conf.getSuperTypeAnn()) != null) || superClass.getAnnotation(conf.getEntityAnn()) != null){ - BeanModel type = classModelFactory.create(superClass, namePrefix); + BeanModel type = classModelFactory.create(superClass, conf.getNamePrefix()); // include fields of supertype model.include(type); } @@ -205,7 +202,7 @@ public class Processor { msg.printMessage(Kind.NOTE, type.getName() + " is processed"); try { String packageName = type.getPackageName(); - String className = packageName + "." + namePrefix + type.getSimpleName(); + String className = packageName + "." + conf.getNamePrefix() + type.getSimpleName(); JavaFileObject fileObject = env.getFiler().createSourceFile(className); Writer writer = fileObject.openWriter(); try { diff --git a/querydsl-apt/src/test/java/com/mysema/query/domain/EntityTest.java b/querydsl-apt/src/test/java/com/mysema/query/domain/EntityTest.java index ebcdfb189..b5a192f7d 100644 --- a/querydsl-apt/src/test/java/com/mysema/query/domain/EntityTest.java +++ b/querydsl-apt/src/test/java/com/mysema/query/domain/EntityTest.java @@ -41,7 +41,7 @@ public class EntityTest { @SuppressWarnings("unchecked") @Test - public void test(){ + public void inheritance(){ assertTrue(QEntity2.entity2 instanceof QSupertype); assertTrue(QEntity3.entity3 instanceof QSupertype); } diff --git a/querydsl-apt/src/test/java/com/mysema/query/domain/InterfaceTypeTest.java b/querydsl-apt/src/test/java/com/mysema/query/domain/InterfaceTypeTest.java index 5d6a5a8aa..a3f82c62b 100644 --- a/querydsl-apt/src/test/java/com/mysema/query/domain/InterfaceTypeTest.java +++ b/querydsl-apt/src/test/java/com/mysema/query/domain/InterfaceTypeTest.java @@ -1,10 +1,14 @@ package com.mysema.query.domain; +import static org.junit.Assert.assertEquals; + import java.util.List; import org.junit.Test; import com.mysema.query.annotations.QueryEntity; +import com.mysema.query.types.path.PEntityList; +import com.mysema.query.types.path.PNumber; public class InterfaceTypeTest { @@ -36,22 +40,57 @@ public class InterfaceTypeTest { } - @Test - public void test() throws SecurityException, NoSuchFieldException{ - Class cl = QInterfaceType.class; - cl.getField("relation"); - cl.getField("relation2"); - cl.getField("relation3"); - cl.getField("relation4"); + @QueryEntity + public interface InterfaceType4{ + + public String getProp4(); + + } + + @QueryEntity + public interface InterfaceType5 extends InterfaceType3, InterfaceType4{ + + public String getProp5(); + } @Test - public void test2() throws SecurityException, NoSuchFieldException{ + public void QInterfaceType_reation() throws SecurityException, NoSuchFieldException{ + assertEquals(QInterfaceType.class, QInterfaceType.class.getField("relation").getType()); + } + + @Test + public void QInterfaceType_reation2() throws SecurityException, NoSuchFieldException{ + assertEquals(PEntityList.class, QInterfaceType.class.getField("relation2").getType()); + } + + @Test + public void QInterfaceType_reation3() throws SecurityException, NoSuchFieldException{ + assertEquals(PEntityList.class, QInterfaceType.class.getField("relation3").getType()); + } + + @Test + public void QInterfaceType_reation4() throws SecurityException, NoSuchFieldException{ + assertEquals(PNumber.class, QInterfaceType.class.getField("relation4").getType()); + } + + @Test + public void testQInterfaceType3() throws SecurityException, NoSuchFieldException{ Class cl = QInterfaceType3.class; cl.getField("prop"); cl.getField("prop2"); cl.getField("prop3"); } + @Test + public void testQInterfaceType5() throws SecurityException, NoSuchFieldException{ + Class cl = QInterfaceType5.class; + cl.getField("prop"); + cl.getField("prop2"); + cl.getField("prop3"); + cl.getField("prop4"); + cl.getField("prop5"); + } + } diff --git a/querydsl-apt/src/test/java/com/mysema/query/domain/RelationTest.java b/querydsl-apt/src/test/java/com/mysema/query/domain/RelationTest.java index b0d6fadda..7aac04709 100644 --- a/querydsl-apt/src/test/java/com/mysema/query/domain/RelationTest.java +++ b/querydsl-apt/src/test/java/com/mysema/query/domain/RelationTest.java @@ -71,7 +71,7 @@ public class RelationTest { @Test public void test(){ - + // TODO } } diff --git a/querydsl-apt/src/test/java/com/mysema/query/domain/ReservedNamesTest.java b/querydsl-apt/src/test/java/com/mysema/query/domain/ReservedNamesTest.java index e8a2e3f52..1a51d05c2 100644 --- a/querydsl-apt/src/test/java/com/mysema/query/domain/ReservedNamesTest.java +++ b/querydsl-apt/src/test/java/com/mysema/query/domain/ReservedNamesTest.java @@ -56,8 +56,18 @@ public class ReservedNamesTest { } @Test - public void test(){ - // TODO + public void test() throws SecurityException, NoSuchFieldException{ + Class cl = QReservedNames.class; + cl.getField("new_"); + cl.getField("package_"); + cl.getField("protected_"); + cl.getField("if_"); + cl.getField("else_"); + cl.getField("try_"); + cl.getField("catch_"); + cl.getField("while_"); + cl.getField("for_"); + cl.getField("extends_"); } } diff --git a/querydsl-apt/src/test/java/com/mysema/query/domain/SimpleTypesTest.java b/querydsl-apt/src/test/java/com/mysema/query/domain/SimpleTypesTest.java index f9b6fd865..76864ec70 100644 --- a/querydsl-apt/src/test/java/com/mysema/query/domain/SimpleTypesTest.java +++ b/querydsl-apt/src/test/java/com/mysema/query/domain/SimpleTypesTest.java @@ -1,5 +1,7 @@ package com.mysema.query.domain; +import static org.junit.Assert.assertEquals; + import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; @@ -9,6 +11,9 @@ import org.junit.Test; import com.mysema.query.annotations.QueryEntity; import com.mysema.query.annotations.QueryTransient; +import com.mysema.query.types.path.PComparable; +import com.mysema.query.types.path.PNumber; +import com.mysema.query.types.path.PSimple; public class SimpleTypesTest { @@ -16,7 +21,42 @@ public class SimpleTypesTest { } - public class CustomComparableLiteral implements Comparable { + @SuppressWarnings("serial") + public static class CustomNumber extends Number{ + + @Override + public double doubleValue() { + return 0; + } + + @Override + public float floatValue() { + return 0; + } + + @Override + public int intValue() { + return 0; + } + + @Override + public long longValue() { + return 0; + } + + } + + @SuppressWarnings("serial") + public static class CustomComparableNumber extends CustomNumber implements Comparable{ + + @Override + public int compareTo(CustomComparableNumber o) { + return 0; + } + + } + + public static class CustomComparableLiteral implements Comparable { @Override public int compareTo(CustomComparableLiteral o) { @@ -27,69 +67,62 @@ public class SimpleTypesTest { @QueryEntity public static class SimpleTypes { - transient int test; - long id; - BigDecimal bigDecimal; - Byte bbyte; - byte bbyte2; - Character cchar; - char cchar2; - Double ddouble; - double ddouble2; - Float ffloat; - float ffloat2; - Integer iint; - int iint2; - Locale llocale; - Long llong; - long llong2; - String sstring; - Date date; - java.sql.Time time; - java.sql.Timestamp timestamp; - Serializable serializable; - Object object; - Class clazz; - Package packageAsLiteral; - - CustomLiteral literal; - - CustomComparableLiteral literal2; - + CustomLiteral customLiteral; + CustomComparableLiteral customComparableLiteral; + CustomNumber customNumber; + CustomComparableNumber customComparableNumber; java.sql.Clob clob; - java.sql.Blob blob; - @QueryTransient String skipMe; } + + @Test + public void customLiteral() throws SecurityException, NoSuchFieldException{ + assertEquals(PSimple.class, QSimpleTypes.class.getField("customLiteral").getType()); + } + + @Test + public void customComparableLiteral() throws SecurityException, NoSuchFieldException{ + assertEquals(PComparable.class, QSimpleTypes.class.getField("customComparableLiteral").getType()); + } + + @Test + public void customNumber() throws SecurityException, NoSuchFieldException{ + assertEquals(PSimple.class, QSimpleTypes.class.getField("customNumber").getType()); + } + + @Test + public void customComparableNumber() throws SecurityException, NoSuchFieldException{ + assertEquals(PNumber.class, QSimpleTypes.class.getField("customComparableNumber").getType()); + } @Test(expected=NoSuchFieldException.class) - public void test() throws SecurityException, NoSuchFieldException { + public void skippedFields() throws SecurityException, NoSuchFieldException { QSimpleTypes.class.getField("skipMe"); } diff --git a/querydsl-core/src/main/java/com/mysema/query/codegen/BeanModel.java b/querydsl-core/src/main/java/com/mysema/query/codegen/BeanModel.java index 2899232df..4d93ec38b 100644 --- a/querydsl-core/src/main/java/com/mysema/query/codegen/BeanModel.java +++ b/querydsl-core/src/main/java/com/mysema/query/codegen/BeanModel.java @@ -10,8 +10,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import javax.annotation.Nullable; - import org.apache.commons.collections15.Factory; import org.apache.commons.collections15.MapUtils; import org.apache.commons.lang.StringUtils; diff --git a/querydsl-core/src/main/java/com/mysema/query/codegen/TypeModelFactory.java b/querydsl-core/src/main/java/com/mysema/query/codegen/TypeModelFactory.java index 814e3b427..ed53b567f 100644 --- a/querydsl-core/src/main/java/com/mysema/query/codegen/TypeModelFactory.java +++ b/querydsl-core/src/main/java/com/mysema/query/codegen/TypeModelFactory.java @@ -81,6 +81,9 @@ public class TypeModelFactory { TypeModel valueInfo = create(TypeUtil.getTypeParameter(genericType, 0)); value = createCollectionType(valueInfo); + }else if (Number.class.isAssignableFrom(cl) && Comparable.class.isAssignableFrom(cl)){ + value = new ClassTypeModel(TypeCategory.NUMERIC, cl); + } else { TypeCategory typeCategory = TypeCategory.get(cl.getName()); if (!typeCategory.isSubCategoryOf(TypeCategory.COMPARABLE) && Comparable.class.isAssignableFrom(cl)){