package net.bytebuddy.description.type;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.test.utility.ObjectPropertyAssertion;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class TypeDescriptionGenericBuilderTest extends AbstractTypeDescriptionGenericTest {
@Override
protected TypeDescription.Generic describeType(Field field) {
return describe(field.getGenericType(), TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveFieldType(field))
.accept(TypeDescription.Generic.Visitor.Substitutor.ForAttachment.of(new FieldDescription.ForLoadedField(field)));
}
@Override
protected TypeDescription.Generic describeReturnType(Method method) {
return describe(method.getGenericReturnType(), TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveReturnType(method))
.accept(TypeDescription.Generic.Visitor.Substitutor.ForAttachment.of(new MethodDescription.ForLoadedMethod(method)));
}
@Override
protected TypeDescription.Generic describeParameterType(Method method, int index) {
return describe(method.getGenericParameterTypes()[index], TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveParameterType(method, index))
.accept(TypeDescription.Generic.Visitor.Substitutor.ForAttachment.of(new MethodDescription.ForLoadedMethod(method)));
}
@Override
protected TypeDescription.Generic describeExceptionType(Method method, int index) {
return describe(method.getGenericExceptionTypes()[index], TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveExceptionType(method, index))
.accept(TypeDescription.Generic.Visitor.Substitutor.ForAttachment.of(new MethodDescription.ForLoadedMethod(method)));
}
@Override
protected TypeDescription.Generic describeSuperClass(Class<?> type) {
return describe(type.getGenericSuperclass(), TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveSuperClassType(type))
.accept(TypeDescription.Generic.Visitor.Substitutor.ForAttachment.of(new TypeDescription.ForLoadedType(type)));
}
@Override
protected TypeDescription.Generic describeInterfaceType(Class<?> type, int index) {
return describe(type.getGenericInterfaces()[index], TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveInterfaceType(type, index))
.accept(TypeDescription.Generic.Visitor.Substitutor.ForAttachment.of(new TypeDescription.ForLoadedType(type)));
}
@Test(expected = IllegalArgumentException.class)
public void testNoOwnerTypeWhenRequired() throws Exception {
TypeDescription.Generic.Builder.parameterizedType(Foo.Inner.class, Object.class);
}
@Test
public void testImplicitOwnerTypeWhenRequired() throws Exception {
assertThat(TypeDescription.Generic.Builder.parameterizedType(Foo.class, Object.class).build().getOwnerType(),
is(TypeDefinition.Sort.describe(getClass())));
}
@Test(expected = IllegalArgumentException.class)
public void testOwnerTypeWhenNotRequired() throws Exception {
TypeDescription.Generic.Builder.parameterizedType(Foo.class, Object.class, Collections.<Type>singletonList(Object.class));
}
@Test(expected = IllegalArgumentException.class)
public void testIllegalOwnerType() throws Exception {
TypeDescription.Generic.Builder.parameterizedType(Foo.Inner.class, Object.class, Collections.<Type>singletonList(Foo.class));
}
@Test(expected = IllegalArgumentException.class)
public void testNonGenericOwnerType() throws Exception {
TypeDescription.Generic.Builder.parameterizedType(Foo.Inner.class, Foo.class, Collections.<Type>singletonList(Foo.class));
}
@Test(expected = IllegalArgumentException.class)
public void testGenericOwnerType() throws Exception {
TypeDescription.Generic.Builder.parameterizedType(new TypeDescription.ForLoadedType(Foo.Nested.class),
TypeDescription.Generic.Builder.parameterizedType(Foo.class, Object.class).build(),
Collections.<TypeDefinition>singletonList(TypeDescription.OBJECT));
}
@Test(expected = IllegalArgumentException.class)
public void testIncompatibleParameterTypeNumber() throws Exception {
TypeDescription.Generic.Builder.parameterizedType(Foo.class);
}
@Test(expected = IllegalArgumentException.class)
public void testForbiddenZeroArity() throws Exception {
TypeDescription.Generic.Builder.rawType(Foo.class).asArray(0);
}
@Test(expected = IllegalArgumentException.class)
public void testForbiddenNegativeType() throws Exception {
TypeDescription.Generic.Builder.rawType(Foo.class).asArray(-1);
}
@Test
public void testMultipleArityArray() throws Exception {
assertThat(TypeDescription.Generic.Builder.rawType(Foo.class).asArray(2).build().getComponentType().getComponentType().represents(Foo.class), is(true));
}
@Test(expected = IllegalArgumentException.class)
public void testCannotAnnotateVoid() throws Exception {
TypeDescription.Generic.Builder.rawType(void.class).annotate(mock(AnnotationDescription.class)).build();
}
@Test(expected = IllegalArgumentException.class)
public void testNonGenericTypeAsParameterizedType() throws Exception {
TypeDescription.Generic.Builder.parameterizedType(Object.class).build();
}
@Test(expected = IllegalArgumentException.class)
public void testMissingOwnerType() throws Exception {
TypeDescription.Generic.Builder.rawType(Bar.Inner.class, TypeDescription.Generic.UNDEFINED);
}
@Test(expected = IllegalArgumentException.class)
public void testIncompatibleType() throws Exception {
TypeDescription.Generic.Builder.rawType(Bar.Inner.class, TypeDescription.Generic.OBJECT);
}
@Test(expected = IllegalArgumentException.class)
public void testIncompatibleOwnerTypeWhenNonRequired() throws Exception {
TypeDescription.Generic.Builder.rawType(Object.class, TypeDescription.Generic.OBJECT);
}
@Test
public void testExplicitOwnerTypeOfNonGenericType() throws Exception {
TypeDescription.Generic ownerType = TypeDescription.Generic.Builder.rawType(Bar.class).build();
TypeDescription.Generic typeDescription = TypeDescription.Generic.Builder.rawType(Bar.Inner.class, ownerType).build();
assertThat(typeDescription.getSort(), is(TypeDefinition.Sort.NON_GENERIC));
assertThat(typeDescription.represents(Bar.Inner.class), is(true));
assertThat(typeDescription.getOwnerType(), sameInstance(ownerType));
}
@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(TypeDescription.Generic.Builder.OfGenericArrayType.class).apply();
ObjectPropertyAssertion.of(TypeDescription.Generic.Builder.OfNonGenericType.class).refine(new ObjectPropertyAssertion.Refinement<TypeDescription>() {
@Override
public void apply(TypeDescription mock) {
when(mock.asGenericType()).thenReturn(Mockito.mock(TypeDescription.Generic.class));
}
}).apply();
ObjectPropertyAssertion.of(TypeDescription.Generic.Builder.OfParameterizedType.class).apply();
ObjectPropertyAssertion.of(TypeDescription.Generic.Builder.OfTypeVariable.class).apply();
}
@Override
@Test
@Ignore("The Java reflection API does not currently support owner types")
public void testTypeAnnotationOwnerType() throws Exception {
super.testTypeAnnotationOwnerType();
}
@Override
@Test
@Ignore("The Java reflection API does not currently support generic inner types")
public void testTypeAnnotationNonGenericInnerType() throws Exception {
super.testTypeAnnotationNonGenericInnerType();
}
private TypeDescription.Generic describe(Type type, TypeDescription.Generic.AnnotationReader annotationReader) {
if (type instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type;
return wildcardType.getLowerBounds().length == 0
? builder(wildcardType.getUpperBounds()[0], annotationReader.ofWildcardUpperBoundType(0)).asWildcardUpperBound(annotationReader.asList())
: builder(wildcardType.getLowerBounds()[0], annotationReader.ofWildcardLowerBoundType(0)).asWildcardLowerBound(annotationReader.asList());
} else {
return builder(type, annotationReader).build();
}
}
private TypeDescription.Generic.Builder builder(Type type, TypeDescription.Generic.AnnotationReader annotationReader) {
if (type instanceof TypeVariable) {
return TypeDescription.Generic.Builder.typeVariable(((TypeVariable<?>) type).getName()).annotate(annotationReader.asList());
} else if (type instanceof Class) {
Class<?> rawType = (Class<?>) type;
return (rawType.isArray()
? builder(rawType.getComponentType(), annotationReader.ofComponentType()).asArray()
: TypeDescription.Generic.Builder.rawType((Class<?>) type)).annotate(annotationReader.asList());
} else if (type instanceof GenericArrayType) {
return builder(((GenericArrayType) type).getGenericComponentType(), annotationReader.ofComponentType()).asArray().annotate(annotationReader.asList());
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
List<TypeDescription.Generic> parameters = new ArrayList<TypeDescription.Generic>(parameterizedType.getActualTypeArguments().length);
int index = 0;
for (Type parameter : parameterizedType.getActualTypeArguments()) {
parameters.add(describe(parameter, annotationReader.ofTypeArgument(index++)));
}
return TypeDescription.Generic.Builder.parameterizedType(new TypeDescription.ForLoadedType((Class<?>) parameterizedType.getRawType()),
parameterizedType.getOwnerType() == null
? null
: describe(parameterizedType.getOwnerType(), annotationReader.ofOwnerType()),
parameters).annotate(annotationReader.asList());
} else {
throw new AssertionError("Unexpected type: " + type);
}
}
@SuppressWarnings("unused")
private static class Foo<T> {
private class Inner<S> {
/* empty */
}
private static class Nested<S> {
/* empty */
}
}
@SuppressWarnings("unused")
private class Bar {
private class Inner {
/* empty */
}
}
}