diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PArray.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PArray.java index e20cf31f0..426f36485 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PArray.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PArray.java @@ -5,6 +5,7 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Array; import javax.annotation.Nonnegative; @@ -116,6 +117,11 @@ public class PArray extends Expr implements Path, EArray{ return pathMixin.isNull(); } + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } + /** * Create an expression for the array size * diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PBoolean.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PBoolean.java index ac4123cd3..b4c1402a3 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PBoolean.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PBoolean.java @@ -5,6 +5,8 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; + import com.mysema.query.types.Visitor; import com.mysema.query.types.expr.EBoolean; @@ -72,4 +74,9 @@ public class PBoolean extends EBoolean implements Path { return pathMixin.isNull(); } + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } + } \ No newline at end of file diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PCollection.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PCollection.java index 6f40cde22..fba8fca51 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PCollection.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PCollection.java @@ -5,6 +5,7 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; import java.util.Collection; import com.mysema.commons.lang.Assert; @@ -90,5 +91,10 @@ public class PCollection extends ECollectionBase,E> implements public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } \ No newline at end of file diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PComparable.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PComparable.java index 55639ebc1..e682538b9 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PComparable.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PComparable.java @@ -5,6 +5,8 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; + import com.mysema.query.types.Visitor; import com.mysema.query.types.expr.EBoolean; import com.mysema.query.types.expr.EComparable; @@ -78,4 +80,9 @@ public class PComparable extends EComparable implements public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } \ No newline at end of file diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PDate.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PDate.java index 44c3ff672..d6ebe611e 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PDate.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PDate.java @@ -5,6 +5,8 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; + import com.mysema.query.types.Visitor; import com.mysema.query.types.expr.EBoolean; import com.mysema.query.types.expr.EDate; @@ -71,5 +73,10 @@ public class PDate extends EDate implements Path{ public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PDateTime.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PDateTime.java index 2b53bc9ec..f98f376b9 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PDateTime.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PDateTime.java @@ -5,6 +5,8 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; + import com.mysema.query.types.Visitor; import com.mysema.query.types.expr.EBoolean; import com.mysema.query.types.expr.EDateTime; @@ -71,4 +73,9 @@ public class PDateTime extends EDateTime implements Pat public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PEntity.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PEntity.java index 9b76db5d4..0de44f7d1 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PEntity.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PEntity.java @@ -5,6 +5,7 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; import java.util.HashMap; import java.util.Map; @@ -270,4 +271,9 @@ public class PEntity extends Expr implements Path { public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } \ No newline at end of file diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PList.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PList.java index 6e41d4959..5df7de86e 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PList.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PList.java @@ -5,6 +5,7 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.HashMap; @@ -142,6 +143,11 @@ public class PList> extends ECollectionBase,E> impl return pathMixin.isNull(); } + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } + private Q newInstance(PathMetadata pm) throws Exception{ if (constructor == null){ try { diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PMap.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PMap.java index da4962789..41d6c07b5 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PMap.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PMap.java @@ -5,6 +5,7 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.HashSet; @@ -134,6 +135,11 @@ public class PMap> extends EMapBase implements Pat return pathMixin.isNull(); } + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } + private E newInstance(PathMetadata pm) throws Exception{ if (constructor == null){ try { diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PNumber.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PNumber.java index 0e8eed2e1..8955fa0a5 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PNumber.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PNumber.java @@ -5,6 +5,8 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; + import com.mysema.query.types.Visitor; import com.mysema.query.types.expr.EBoolean; import com.mysema.query.types.expr.ENumber; @@ -73,4 +75,9 @@ public class PNumber> extends ENumber implem public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } \ No newline at end of file diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PSet.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PSet.java index e8e6bae37..332e2a4fa 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PSet.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PSet.java @@ -5,6 +5,7 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; import java.util.Set; import com.mysema.commons.lang.Assert; @@ -90,5 +91,10 @@ public class PSet extends ECollectionBase,E> implements Path> { public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PSimple.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PSimple.java index 9d06c6c29..f1a500078 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PSimple.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PSimple.java @@ -5,6 +5,8 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; + import com.mysema.query.types.Visitor; import com.mysema.query.types.expr.EBoolean; import com.mysema.query.types.expr.Expr; @@ -73,4 +75,9 @@ public class PSimple extends Expr implements Path { public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } \ No newline at end of file diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PString.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PString.java index c68fecdde..1595e5a1a 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PString.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PString.java @@ -5,6 +5,8 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; + import com.mysema.query.types.Visitor; import com.mysema.query.types.expr.EBoolean; import com.mysema.query.types.expr.EString; @@ -71,4 +73,9 @@ public class PString extends EString implements Path { public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } \ No newline at end of file diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PTime.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PTime.java index ea6149abc..d0070e5ed 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PTime.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PTime.java @@ -5,6 +5,8 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; + import com.mysema.query.types.Visitor; import com.mysema.query.types.expr.EBoolean; import com.mysema.query.types.expr.ETime; @@ -70,4 +72,9 @@ public class PTime extends ETime implements Path{ public EBoolean isNull() { return pathMixin.isNull(); } + + @Override + public AnnotatedElement getAnnotatedElement(){ + return pathMixin.getAnnotatedElement(); + } } diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/Path.java b/querydsl-core/src/main/java/com/mysema/query/types/path/Path.java index c0f011c46..9ed4d57cb 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/Path.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/Path.java @@ -5,6 +5,8 @@ */ package com.mysema.query.types.path; +import java.lang.reflect.AnnotatedElement; + import com.mysema.query.types.expr.EBoolean; import com.mysema.query.types.expr.Expr; @@ -55,5 +57,15 @@ public interface Path { * @return */ EBoolean isNull(); + + /** + * Return the annotated element related to the given path + * For property paths the annotated element contains the annotations of the + * related field and/or getter method and for all others paths the annotated element + * is the expression type. + * + * @return + */ + AnnotatedElement getAnnotatedElement(); } diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PathMetadata.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PathMetadata.java index f36a4cb9d..14d92dfb2 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PathMetadata.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PathMetadata.java @@ -6,8 +6,6 @@ package com.mysema.query.types.path; import java.io.Serializable; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; import javax.annotation.Nullable; @@ -38,8 +36,6 @@ public final class PathMetadata implements Serializable{ private final PathType pathType; -// private transient AnnotatedElement annotatedElement; - PathMetadata(@Nullable Path parent, Expr expression, PathType type) { this.parent = parent; this.expression = expression; @@ -87,35 +83,5 @@ public final class PathMetadata implements Serializable{ public boolean isRoot(){ return parent == null; } -// -// private AnnotatedElement getAnnotatedElement(){ -// if (annotatedElement == null){ -// if (pathType == PathType.VARIABLE){ -// annotatedElement = -// } -// } -// return annotatedElement; -// } -// -// @SuppressWarnings("hiding") -// @Override -// public T getAnnotation(Class annotationClass) { -// return getAnnotatedElement().getAnnotation(annotationClass); -// } -// -// @Override -// public Annotation[] getAnnotations() { -// return getAnnotatedElement().getAnnotations(); -// } -// -// @Override -// public Annotation[] getDeclaredAnnotations() { -// return getAnnotatedElement().getDeclaredAnnotations(); -// } -// -// @Override -// public boolean isAnnotationPresent(Class annotationClass) { -// return getAnnotatedElement().isAnnotationPresent(annotationClass); -// } - + } diff --git a/querydsl-core/src/main/java/com/mysema/query/types/path/PathMixin.java b/querydsl-core/src/main/java/com/mysema/query/types/path/PathMixin.java index 601d03c65..ad7dc3002 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/path/PathMixin.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/path/PathMixin.java @@ -6,11 +6,13 @@ package com.mysema.query.types.path; import java.io.Serializable; +import java.lang.reflect.AnnotatedElement; import com.mysema.query.types.expr.EBoolean; import com.mysema.query.types.expr.Expr; import com.mysema.query.types.operation.OBoolean; import com.mysema.query.types.operation.Ops; +import com.mysema.util.PropertyUtils; /** * PathMixin defines a mixin version of the Path interface which can be used @@ -31,6 +33,8 @@ class PathMixin implements Path, Serializable { private final Expr self; + private AnnotatedElement annotatedElement; + @SuppressWarnings("unchecked") public PathMixin(Expr self, PathMetadata metadata){ this.self = self; @@ -84,4 +88,20 @@ class PathMixin implements Path, Serializable { return isnull; } + @Override + public AnnotatedElement getAnnotatedElement() { + if (annotatedElement == null){ + if (metadata.getPathType() == PathType.PROPERTY){ + Class beanClass = metadata.getParent().getType(); + String propertyName = metadata.getExpression().toString(); + annotatedElement = PropertyUtils.getAnnotatedElement(beanClass, propertyName, self.getType()); + + }else{ + annotatedElement = self.getType(); + } + } + return annotatedElement; + } + + } diff --git a/querydsl-core/src/main/java/com/mysema/util/AnnotatedElementAdapter.java b/querydsl-core/src/main/java/com/mysema/util/AnnotatedElementAdapter.java new file mode 100644 index 000000000..93c2ad333 --- /dev/null +++ b/querydsl-core/src/main/java/com/mysema/util/AnnotatedElementAdapter.java @@ -0,0 +1,45 @@ +package com.mysema.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.HashMap; +import java.util.Map; + +/** + * @author tiwe + * + */ +public class AnnotatedElementAdapter implements AnnotatedElement{ + + private final Map,Annotation> annotations = new HashMap,Annotation>(); + + public AnnotatedElementAdapter(AnnotatedElement... elements){ + for (AnnotatedElement element : elements){ + for (Annotation annotation : element.getAnnotations()){ + annotations.put(annotation.annotationType(), annotation); + } + } + } + + @SuppressWarnings("unchecked") + @Override + public T getAnnotation(Class annotationClass) { + return (T) annotations.get(annotationClass); + } + + @Override + public Annotation[] getAnnotations() { + return annotations.values().toArray(new Annotation[annotations.values().size()]); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return getAnnotations(); + } + + @Override + public boolean isAnnotationPresent(Class annotationClass) { + return annotations.containsKey(annotationClass); + } + +} diff --git a/querydsl-core/src/main/java/com/mysema/util/PropertyUtils.java b/querydsl-core/src/main/java/com/mysema/util/PropertyUtils.java new file mode 100644 index 000000000..661bf35b5 --- /dev/null +++ b/querydsl-core/src/main/java/com/mysema/util/PropertyUtils.java @@ -0,0 +1,60 @@ +package com.mysema.util; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import javax.annotation.Nullable; + +import com.sun.xml.internal.ws.util.StringUtils; + +/** + * @author tiwe + * + */ +public final class PropertyUtils { + + private PropertyUtils(){} + + @Nullable + public static AnnotatedElement getAnnotatedElement(Class beanClass, String propertyName, Class propertyClass){ + Field field = getField(beanClass, propertyName); + Method method = getGetter(beanClass, propertyName, propertyClass); + if (field == null || field.getAnnotations().length == 0){ + return method; + }else if (method == null || method.getAnnotations().length == 0){ + return field; + }else{ + return new AnnotatedElementAdapter(field, method); + } + } + + @Nullable + private static Field getField(Class beanClass, String propertyName){ + while (!beanClass.equals(Object.class)){ + try { + return beanClass.getDeclaredField(propertyName); + } catch (SecurityException e) { // skip + } catch (NoSuchFieldException e) { // skip + } + beanClass = beanClass.getSuperclass(); + } + return null; + } + + @Nullable + private static Method getGetter(Class beanClass, String name, Class type){ + String methodName = (type.equals(Boolean.class) ? "is" : "get") + StringUtils.capitalize(name); + while(!beanClass.equals(Object.class)){ + try { + return beanClass.getDeclaredMethod(methodName); + } catch (SecurityException e) { // skip + } catch (NoSuchMethodException e) { // skip + } + beanClass = beanClass.getSuperclass(); + } + return null; + + } + +} diff --git a/querydsl-core/src/test/java/com/mysema/query/types/path/PathTest.java b/querydsl-core/src/test/java/com/mysema/query/types/path/PathTest.java new file mode 100644 index 000000000..f453876bc --- /dev/null +++ b/querydsl-core/src/test/java/com/mysema/query/types/path/PathTest.java @@ -0,0 +1,109 @@ +package com.mysema.query.types.path; + +import static com.mysema.query.alias.Alias.$; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.junit.Test; + +import com.mysema.query.alias.Alias; +import com.mysema.query.annotations.QueryEntity; +import com.mysema.query.annotations.QueryTransient; +import com.mysema.util.AnnotatedElementAdapter; + +public class PathTest { + + public static class Superclass { + + @Nullable + public String getProperty4(){ + return null; + } + } + + @QueryEntity + public static class Entity extends Superclass{ + + @Nullable + private String property1; + + private String property2; + + @QueryTransient + private String property3; + + public String getProperty1(){ + return property1; + } + + @Nonnull + public String getProperty2(){ + return property2; + } + + @Nonnull + public String getProperty3(){ + return property3; + } + + + } + + @Test + public void getAnnotatedElement(){ + Entity entity = Alias.alias(Entity.class); + + AnnotatedElement element = $(entity).getAnnotatedElement(); + + // type + assertEquals(Entity.class, element); + } + + @Test + public void getAnnotatedElement_for_property(){ + Entity entity = Alias.alias(Entity.class); + AnnotatedElement property1 = $(entity.getProperty1()).getAnnotatedElement(); + AnnotatedElement property2 = $(entity.getProperty2()).getAnnotatedElement(); + AnnotatedElement property3 = $(entity.getProperty3()).getAnnotatedElement(); + AnnotatedElement property4 = $(entity.getProperty4()).getAnnotatedElement(); + + // property (field) + assertEquals(Field.class, property1.getClass()); + assertTrue(property1.isAnnotationPresent(Nullable.class)); + assertNotNull(property1.getAnnotation(Nullable.class)); + assertFalse(property1.isAnnotationPresent(Nonnull.class)); + assertNull(property1.getAnnotation(Nonnull.class)); + + // property2 (method) + assertEquals(Method.class, property2.getClass()); + assertTrue(property2.isAnnotationPresent(Nonnull.class)); + assertNotNull(property2.getAnnotation(Nonnull.class)); + assertFalse(property2.isAnnotationPresent(Nullable.class)); + assertNull(property2.getAnnotation(Nullable.class)); + + // property3 (both) + assertEquals(AnnotatedElementAdapter.class, property3.getClass()); + assertTrue(property3.isAnnotationPresent(QueryTransient.class)); + assertNotNull(property3.getAnnotation(QueryTransient.class)); + assertTrue(property3.isAnnotationPresent(Nonnull.class)); + assertNotNull(property3.getAnnotation(Nonnull.class)); + assertFalse(property3.isAnnotationPresent(Nullable.class)); + assertNull(property3.getAnnotation(Nullable.class)); + + // property 4 (superclass) + assertEquals(Method.class, property4.getClass()); + assertTrue(property4.isAnnotationPresent(Nullable.class)); + assertNotNull(property4.getAnnotation(Nullable.class)); + + } +}