Skip to content

Commit

Permalink
WELD-1111 Inheritance of member-level metadata for generic types - mi…
Browse files Browse the repository at this point in the history
…ssing substitution of actual type arguments
  • Loading branch information
luksa authored and alesj committed Apr 23, 2012
1 parent e9b820e commit f1ac690
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ protected ObserverMethodImpl(final WeldMethod<T, ? super X> observer, final RIBe
this.observerMethod = MethodInjectionPoint.of(declaringBean, observer);

WeldParameter<?, ? super X> eventArgument = observerMethod.getAnnotatedParameters(Observes.class).get(0);
this.eventType = eventArgument.getBaseType();
this.eventType = TypeVariableResolver.resolveVariables(declaringBean, eventArgument.getBaseType());
this.id = ID_PREFIX + ID_SEPARATOR + /*manager.getId() + ID_SEPARATOR +*/ ObserverMethod.class.getSimpleName() + ID_SEPARATOR + declaringBean.getBeanClass().getName() + "." + observer.getSignature();
this.bindings = new HashSet<Annotation>(eventArgument.getMetaAnnotations(Qualifier.class));
this.reception = eventArgument.getAnnotation(Observes.class).notifyObserver();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.util.AnnotatedTypes;
import org.jboss.weld.util.reflection.Reflections;
import org.jboss.weld.util.reflection.TypeVariableResolver;

import static org.jboss.weld.injection.Exceptions.rethrowException;
import static org.jboss.weld.logging.messages.BeanMessage.PROXY_REQUIRED;
Expand All @@ -57,6 +58,7 @@ public class FieldInjectionPoint<T, X> extends ForwardingWeldField<T, X> impleme
private final boolean delegate;
private final boolean cacheable;
private Bean<?> cachedBean;
private Type type;


public static <T, X> FieldInjectionPoint<T, X> of(Bean<?> declaringBean, WeldField<T, X> field) {
Expand Down Expand Up @@ -155,7 +157,10 @@ public boolean isDelegate() {
}

public Type getType() {
return getBaseType();
if (type == null) {
type = TypeVariableResolver.resolveVariables(getBean(), getBaseType());
}
return type;
}

public Member getMember() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.jboss.weld.util.reflection;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Type;

/**
* @author Marko Luksa
*/
class GenericArrayTypeImpl implements GenericArrayType {

private Type genericComponentType;

GenericArrayTypeImpl(Type genericComponentType) {
this.genericComponentType = genericComponentType;
}

public Type getGenericComponentType() {
return genericComponentType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package org.jboss.weld.util.reflection;

import javax.enterprise.inject.spi.Bean;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;

/**
* @author Marko Luksa
*/
public class TypeVariableResolver {

private Class beanClass;
private HashMap<TypeVariable,Type> resolvedVariables;

public TypeVariableResolver(Class beanClass) {
if (beanClass == null) {
throw new IllegalArgumentException("beanClass should not be null");
}
this.beanClass = beanClass;
}

public static Type resolveVariables(Bean bean, Type type) {
if (bean == null) {
// bean is null when we're dealing with an InjectionTarget created through BeanManager.createInjectionTarget()
// we can't resolve variables since we're missing critical info, thus we simply return the original type for now
return type;
}
if (bean.getBeanClass() == null) {
throw new IllegalArgumentException("Bean " + bean + " has null beanClass!");
}
return new TypeVariableResolver(bean.getBeanClass()).resolveVariablesInType(type);
}

public Type resolveVariablesInType(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
return new ParameterizedTypeImpl(
parameterizedType.getRawType(),
resolveVariablesInTypes(parameterizedType.getActualTypeArguments()),
parameterizedType.getOwnerType());
} else if (type instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) type;
return resolveTypeVariable(typeVariable);
} else if (type instanceof GenericArrayType) {
GenericArrayType genericArrayType = (GenericArrayType) type;
return new GenericArrayTypeImpl(resolveVariablesInType(genericArrayType.getGenericComponentType()));
} else {
return type;
}
}

private Type[] resolveVariablesInTypes(Type[] types) {
Type[] resolvedTypes = new Type[types.length];
for (int i = 0; i < types.length; i++) {
resolvedTypes[i] = resolveVariablesInType(types[i]);
}
return resolvedTypes;
}

private Type resolveTypeVariable(TypeVariable typeVariable) {
if (resolvedVariables == null) {
resolvedVariables = new HashMap<TypeVariable, Type>();
fillResolvedVariablesMap(beanClass);
}
return resolve(typeVariable); // instead of resolving every time, we could also resolve all entries immediately after filling map
}

private void fillResolvedVariablesMap(Class beanClass) {
Type genericSuperclass = beanClass.getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
ParameterizedType parameterizedSuperClassType = (ParameterizedType) genericSuperclass;

Type[] actualTypeArguments = parameterizedSuperClassType.getActualTypeArguments();
TypeVariable[] typeParameters = beanClass.getSuperclass().getTypeParameters();
for (int i = 0; i < typeParameters.length; i++) {
resolvedVariables.put(typeParameters[i], actualTypeArguments[i]);
}
}

if (beanClass.getSuperclass() != null) {
fillResolvedVariablesMap(beanClass.getSuperclass());
}
}

private Type resolve(Type type) {
if (type instanceof TypeVariable) {
TypeVariable typeVariable = (TypeVariable) type;
if (resolvedVariables.containsKey(typeVariable)) {
return resolve(resolvedVariables.get(typeVariable));
}
}
return type;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.jboss.weld.tests.unit.reflection.inheritance;

/**
*
*/
public class A<E1, E2> {

private Faz faz;

private E1 e1;

private Foo<E1> foo1;

private Foo<E2> foo2;

private Foo<String> stringFoo;

private Foo<Foo<String>> stringFooFoo;

private Foo<Foo<E1>> variableFooFoo;

private Foo<String>[] stringFooArray;

private E1[] variableArray;

private Foo<E1>[] foo1Array;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.jboss.weld.tests.unit.reflection.inheritance;

/**
*
*/
public class B<T,U> extends A<T, Double> {

private Foo<U> foo3;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.jboss.weld.tests.unit.reflection.inheritance;

/**
*
*/
public class BOfIntegerString extends B<Integer, String> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.jboss.weld.tests.unit.reflection.inheritance;

/**
*
*/
public class C<U,T> extends B<T, U> { // note the flipped arguments!

private Foo<U> foo4;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.jboss.weld.tests.unit.reflection.inheritance;

/**
*
*/
public class COfByteShort extends C<Byte, Short> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.jboss.weld.tests.unit.reflection.inheritance;

/**
*
*/
public class Faz {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.jboss.weld.tests.unit.reflection.inheritance;

/**
*
*/
public class Foo<T> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.jboss.weld.tests.unit.reflection.inheritance;

import org.jboss.weld.util.reflection.TypeVariableResolver;
import org.junit.Test;

import javax.enterprise.util.TypeLiteral;
import java.lang.reflect.Field;
import java.lang.reflect.Type;

import static junit.framework.Assert.assertEquals;

/**
* @author <a href="mailto:marko.luksa@gmail.com">Marko Luksa</a>
*/
public class TypeVariableResolverTest {

@Test
public void testConcreteType() throws Exception {
assertTypeEquals(new TypeLiteral<Faz>() { }, A.class, A.class.getDeclaredField("faz"));
}

@Test
public void testParameterizedConcreteType() throws Exception {
assertTypeEquals(new TypeLiteral<Foo<String>>() { }, A.class, A.class.getDeclaredField("stringFoo"));
}

@Test
public void testNestedParameterizedType() throws Exception {
assertTypeEquals(new TypeLiteral<Foo<Foo<String>>>() { }, A.class, A.class.getDeclaredField("stringFooFoo"));
}

@Test
public void testNestedParameterizedTypeWithVariable() throws Exception {
assertTypeEquals(new TypeLiteral<Foo<Foo<Integer>>>() { }, BOfIntegerString.class, A.class.getDeclaredField("variableFooFoo"));
}

@Test
public void testParameterizedTypeWithVariable() throws Exception {
assertTypeEquals(new TypeLiteral<Foo<String>>() { }, BOfIntegerString.class, B.class.getDeclaredField("foo3"));
}

@Test
public void testSuperSuperClass() throws Exception {
assertTypeEquals(new TypeLiteral<Foo<Integer>>() {}, BOfIntegerString.class, A.class.getDeclaredField("foo1"));
}

@Test
public void testSuperSuperClassWhereVariableIsDefinedInSuperClass() throws Exception {
assertTypeEquals(new TypeLiteral<Foo<Double>>() {}, BOfIntegerString.class, A.class.getDeclaredField("foo2"));
}

@Test
public void testStringFooArray() throws Exception {
assertTypeEquals(new TypeLiteral<Foo<String>[]>() {}, BOfIntegerString.class, A.class.getDeclaredField("stringFooArray"));
}

@Test
public void testVariableArray() throws Exception {
assertTypeEquals(new TypeLiteral<Integer[]>() {}, BOfIntegerString.class, A.class.getDeclaredField("variableArray"));
}

@Test
public void testArrayWithParameterizedTypeWithVariable() throws Exception {
assertTypeEquals(new TypeLiteral<Foo<Integer>[]>() {}, BOfIntegerString.class, A.class.getDeclaredField("foo1Array"));
}

@Test
public void testVariable() throws Exception {
assertTypeEquals(new TypeLiteral<Integer>() {}, BOfIntegerString.class, A.class.getDeclaredField("e1"));
}

@Test
public void testSuperSuperSuperClass() throws Exception {
assertTypeEquals(new TypeLiteral<Foo<Short>>() {}, COfByteShort.class, A.class.getDeclaredField("foo1"));
}

private void assertTypeEquals(TypeLiteral<?> expectedTypeLiteral, Class beanClass, Field field) {
Type expectedType = expectedTypeLiteral.getType();
Type type = new TypeVariableResolver(beanClass).resolveVariablesInType(field.getGenericType());
assertEquals(expectedType, type);
}

}

0 comments on commit f1ac690

Please sign in to comment.