#517618 : added support for annotation access

This commit is contained in:
Timo Westkämper 2010-02-05 20:00:04 +00:00
parent 46f905d9ea
commit 09191406f8
20 changed files with 339 additions and 35 deletions

View File

@ -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<E> extends Expr<E[]> implements Path<E[]>, EArray<E>{
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
/**
* Create an expression for the array size
*

View File

@ -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<Boolean> {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<E> extends ECollectionBase<Collection<E>,E> implements
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<D extends Comparable> extends EComparable<D> implements
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<D extends Comparable> extends EDate<D> implements Path<D>{
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<D extends Comparable> extends EDateTime<D> implements Pat
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<D> extends Expr<D> implements Path<D> {
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<E, Q extends Expr<E>> extends ECollectionBase<List<E>,E> impl
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
private Q newInstance(PathMetadata<?> pm) throws Exception{
if (constructor == null){
try {

View File

@ -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<K, V, E extends Expr<V>> extends EMapBase<K, V> implements Pat
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
private E newInstance(PathMetadata<?> pm) throws Exception{
if (constructor == null){
try {

View File

@ -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<D extends Number & Comparable<?>> extends ENumber<D> implem
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<E> extends ECollectionBase<Set<E>,E> implements Path<Set<E>> {
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<D> extends Expr<D> implements Path<D> {
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<String> {
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<D extends Comparable> extends ETime<D> implements Path<D>{
public EBoolean isNull() {
return pathMixin.isNull();
}
@Override
public AnnotatedElement getAnnotatedElement(){
return pathMixin.getAnnotatedElement();
}
}

View File

@ -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<C> {
* @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();
}

View File

@ -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<T> implements Serializable{
private final PathType pathType;
// private transient AnnotatedElement annotatedElement;
PathMetadata(@Nullable Path<?> parent, Expr<T> expression, PathType type) {
this.parent = parent;
this.expression = expression;
@ -87,35 +83,5 @@ public final class PathMetadata<T> 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 extends Annotation> T getAnnotation(Class<T> annotationClass) {
// return getAnnotatedElement().getAnnotation(annotationClass);
// }
//
// @Override
// public Annotation[] getAnnotations() {
// return getAnnotatedElement().getAnnotations();
// }
//
// @Override
// public Annotation[] getDeclaredAnnotations() {
// return getAnnotatedElement().getDeclaredAnnotations();
// }
//
// @Override
// public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
// return getAnnotatedElement().isAnnotationPresent(annotationClass);
// }
}

View File

@ -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<T> implements Path<T>, Serializable {
private final Expr<T> self;
private AnnotatedElement annotatedElement;
@SuppressWarnings("unchecked")
public PathMixin(Expr<T> self, PathMetadata<?> metadata){
this.self = self;
@ -84,4 +88,20 @@ class PathMixin<T> implements Path<T>, 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;
}
}

View File

@ -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<Class<?>,Annotation> annotations = new HashMap<Class<?>,Annotation>();
public AnnotatedElementAdapter(AnnotatedElement... elements){
for (AnnotatedElement element : elements){
for (Annotation annotation : element.getAnnotations()){
annotations.put(annotation.annotationType(), annotation);
}
}
}
@SuppressWarnings("unchecked")
@Override
public <T extends Annotation> T getAnnotation(Class<T> 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<? extends Annotation> annotationClass) {
return annotations.containsKey(annotationClass);
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}