package net.bytebuddy.description.annotation;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.enumeration.EnumerationDescription;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.method.ParameterDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeVariableToken;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.pool.TypePool;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public abstract class AbstractAnnotationDescriptionTest {
private static final boolean BOOLEAN = true;
private static final boolean[] BOOLEAN_ARRAY = new boolean[]{BOOLEAN};
private static final byte BYTE = 42;
private static final byte[] BYTE_ARRAY = new byte[]{BYTE};
private static final short SHORT = 42;
private static final short[] SHORT_ARRAY = new short[]{SHORT};
private static final char CHARACTER = 42;
private static final char[] CHARACTER_ARRAY = new char[]{CHARACTER};
private static final int INTEGER = 42;
private static final int[] INTEGER_ARRAY = new int[]{INTEGER};
private static final long LONG = 42L;
private static final long[] LONG_ARRAY = new long[]{LONG};
private static final float FLOAT = 42f;
private static final float[] FLOAT_ARRAY = new float[]{FLOAT};
private static final double DOUBLE = 42d;
private static final double[] DOUBLE_ARRAY = new double[]{DOUBLE};
private static final String FOO = "foo", BAR = "bar";
private static final String[] STRING_ARRAY = new String[]{FOO};
private static final SampleEnumeration ENUMERATION = SampleEnumeration.VALUE;
private static final SampleEnumeration[] ENUMERATION_ARRAY = new SampleEnumeration[]{ENUMERATION};
private static final Class<?> CLASS = Void.class;
private static final Class<?>[] CLASS_ARRAY = new Class<?>[]{CLASS};
private static final Class<?> ARRAY_CLASS = Void[].class;
private static final Other ANNOTATION = EnumerationCarrier.class.getAnnotation(Other.class);
private static final Other[] ANNOTATION_ARRAY = new Other[]{ANNOTATION};
private static final boolean OTHER_BOOLEAN = false;
private static final boolean[] OTHER_BOOLEAN_ARRAY = new boolean[]{OTHER_BOOLEAN};
private static final byte OTHER_BYTE = 42 * 2;
private static final byte[] OTHER_BYTE_ARRAY = new byte[]{OTHER_BYTE};
private static final short OTHER_SHORT = 42 * 2;
private static final short[] OTHER_SHORT_ARRAY = new short[]{OTHER_SHORT};
private static final char OTHER_CHARACTER = 42 * 2;
private static final char[] OTHER_CHARACTER_ARRAY = new char[]{OTHER_CHARACTER};
private static final int OTHER_INTEGER = 42 * 2;
private static final int[] OTHER_INTEGER_ARRAY = new int[]{OTHER_INTEGER};
private static final long OTHER_LONG = 42L * 2;
private static final long[] OTHER_LONG_ARRAY = new long[]{OTHER_LONG};
private static final float OTHER_FLOAT = 42f * 2;
private static final float[] OTHER_FLOAT_ARRAY = new float[]{OTHER_FLOAT};
private static final double OTHER_DOUBLE = 42d * 2;
private static final double[] OTHER_DOUBLE_ARRAY = new double[]{OTHER_DOUBLE};
private static final SampleEnumeration OTHER_ENUMERATION = SampleEnumeration.OTHER;
private static final SampleEnumeration[] OTHER_ENUMERATION_ARRAY = new SampleEnumeration[]{OTHER_ENUMERATION};
private static final Class<?> OTHER_CLASS = Object.class;
private static final Class<?>[] OTHER_CLASS_ARRAY = new Class<?>[]{OTHER_CLASS};
private static final Class<?> OTHER_ARRAY_CLASS = Object[].class;
private static final Other OTHER_ANNOTATION = OtherEnumerationCarrier.class.getAnnotation(Other.class);
private static final Other[] OTHER_ANNOTATION_ARRAY = new Other[]{OTHER_ANNOTATION};
private static final String[] OTHER_STRING_ARRAY = new String[]{BAR};
private Annotation first, second, defaultFirst, defaultSecond, explicitTarget, broken;
private Class<?> brokenCarrier;
protected abstract AnnotationDescription describe(Annotation annotation, Class<?> declaringType);
private AnnotationDescription describe(Annotation annotation) {
Class<?> carrier;
if (annotation == first) {
carrier = FooSample.class;
} else if (annotation == second) {
carrier = BarSample.class;
} else if (annotation == defaultFirst) {
carrier = DefaultSample.class;
} else if (annotation == defaultSecond) {
carrier = NonDefaultSample.class;
} else if (annotation == explicitTarget) {
carrier = ExplicitTarget.Carrier.class;
} else if (annotation == broken) {
carrier = brokenCarrier;
} else {
throw new AssertionError();
}
return describe(annotation, carrier);
}
@Before
public void setUp() throws Exception {
first = FooSample.class.getAnnotation(Sample.class);
second = BarSample.class.getAnnotation(Sample.class);
defaultFirst = DefaultSample.class.getAnnotation(SampleDefault.class);
defaultSecond = NonDefaultSample.class.getAnnotation(SampleDefault.class);
explicitTarget = ExplicitTarget.Carrier.class.getAnnotation(ExplicitTarget.class);
brokenCarrier = new ByteBuddy()
.subclass(Object.class)
.visit(new AnnotationValueBreaker())
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
.getLoaded();
broken = brokenCarrier.getAnnotations()[0];
}
@Test
public void testPrecondition() throws Exception {
assertThat(describe(first), is(describe(first)));
assertThat(describe(second), is(describe(second)));
assertThat(describe(first), not(describe(second)));
assertThat(describe(first).getAnnotationType(), is(describe(second).getAnnotationType()));
assertThat(describe(first).getAnnotationType(), not((TypeDescription) new TypeDescription.ForLoadedType(Other.class)));
assertThat(describe(second).getAnnotationType(), not((TypeDescription) new TypeDescription.ForLoadedType(Other.class)));
assertThat(describe(first).getAnnotationType().represents(first.annotationType()), is(true));
assertThat(describe(second).getAnnotationType().represents(second.annotationType()), is(true));
}
@Test
public void assertToString() throws Exception {
assertToString(describe(first).toString(), first);
assertToString(describe(second).toString(), second);
}
private void assertToString(String actual, Annotation loaded) throws Exception {
assertThat(actual, startsWith("@" + loaded.annotationType().getName()));
String loadedString = loaded.toString();
if (loadedString.length() - loadedString.replace(",", "").length() != loaded.annotationType().getDeclaredMethods().length - 1) {
throw new AssertionError("Unexpected amount of commas for " + loaded); // Expect tested annotations not to contain commas in values.
}
for (Method method : loaded.annotationType().getDeclaredMethods()) {
assertThat(loadedString.split(method.getName() + "=", -1).length - 1, is(1)); // Expect property delimiter not to exist as value.
int start = loadedString.indexOf(method.getName() + "="), end = loadedString.indexOf(',', start);
assertThat(actual, containsString(loadedString.substring(start, end == -1 ? loadedString.length() - 1 : end)));
}
}
@Test
public void testHashCode() throws Exception {
assertThat(describe(first).hashCode(), is(describe(first).hashCode()));
assertThat(describe(second).hashCode(), is(describe(second).hashCode()));
assertThat(describe(first).hashCode(), not(describe(second).hashCode()));
}
@Test
@SuppressWarnings("unchecked")
public void testEquals() throws Exception {
AnnotationDescription identical = describe(first);
assertThat(identical, is(identical));
AnnotationDescription equalFirst = mock(AnnotationDescription.class);
when(equalFirst.getAnnotationType()).thenReturn(new TypeDescription.ForLoadedType(first.annotationType()));
when(equalFirst.getValue(Mockito.any(MethodDescription.InDefinedShape.class))).then(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
MethodDescription.InDefinedShape method = (MethodDescription.InDefinedShape) invocation.getArguments()[0];
return AnnotationDescription.ForLoadedAnnotation.of(first).getValue(method);
}
});
assertThat(describe(first), is(equalFirst));
AnnotationDescription equalSecond = mock(AnnotationDescription.class);
when(equalSecond.getAnnotationType()).thenReturn(new TypeDescription.ForLoadedType(first.annotationType()));
when(equalSecond.getValue(Mockito.any(MethodDescription.InDefinedShape.class))).then(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
MethodDescription.InDefinedShape method = (MethodDescription.InDefinedShape) invocation.getArguments()[0];
return AnnotationDescription.ForLoadedAnnotation.of(second).getValue(method);
}
});
assertThat(describe(second), is(equalSecond));
AnnotationDescription equalFirstTypeOnly = mock(AnnotationDescription.class);
when(equalFirstTypeOnly.getAnnotationType()).thenReturn(new TypeDescription.ForLoadedType(Other.class));
when(equalFirstTypeOnly.getValue(Mockito.any(MethodDescription.InDefinedShape.class))).then(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
MethodDescription.InDefinedShape method = (MethodDescription.InDefinedShape) invocation.getArguments()[0];
return AnnotationDescription.ForLoadedAnnotation.of(first).getValue(method);
}
});
assertThat(describe(first), not(equalFirstTypeOnly));
AnnotationDescription equalFirstNameOnly = mock(AnnotationDescription.class);
when(equalFirstNameOnly.getAnnotationType()).thenReturn(new TypeDescription.ForLoadedType(first.annotationType()));
AnnotationValue<?, ?> annotationValue = mock(AnnotationValue.class);
when(annotationValue.resolve()).thenReturn(null);
when(equalFirstNameOnly.getValue(Mockito.any(MethodDescription.InDefinedShape.class))).thenReturn((AnnotationValue) annotationValue);
assertThat(describe(first), not(equalFirstNameOnly));
assertThat(describe(first), not(equalSecond));
assertThat(describe(first), not(new Object()));
assertThat(describe(first), not(equalTo(null)));
}
@Test(expected = IllegalArgumentException.class)
public void testIllegalMethod() throws Exception {
describe(first).getValue(new MethodDescription.ForLoadedMethod(Object.class.getMethod("toString")));
}
@Test
public void testLoadedEquals() throws Exception {
assertThat(describe(first).prepare(Sample.class).load(), is(first));
assertThat(describe(first).prepare(Sample.class).load(), is(describe(first).prepare(Sample.class).load()));
assertThat(describe(first).prepare(Sample.class).load(), not(describe(second).prepare(Sample.class).load()));
assertThat(describe(second).prepare(Sample.class).load(), is(second));
assertThat(describe(first).prepare(Sample.class).load(), not(second));
}
@Test
public void testLoadedHashCode() throws Exception {
assertThat(describe(first).prepare(Sample.class).load().hashCode(), is(first.hashCode()));
assertThat(describe(second).prepare(Sample.class).load().hashCode(), is(second.hashCode()));
assertThat(describe(first).prepare(Sample.class).load().hashCode(), not(second.hashCode()));
}
@Test
public void testLoadedToString() throws Exception {
assertToString(describe(first).prepare(Sample.class).load().toString(), first);
assertToString(describe(second).prepare(Sample.class).load().toString(), second);
}
@Test
public void testToString() throws Exception {
assertToString(describe(first).prepare(Sample.class).toString(), first);
assertToString(describe(second).prepare(Sample.class).toString(), second);
}
@Test
@Ignore("Add better handling for annotations with inconsistent values")
public void testBrokenAnnotation() throws Exception {
describe(broken);
}
@Test
public void testLoadedAnnotationType() throws Exception {
assertThat(describe(first).prepare(Sample.class).load().annotationType(), CoreMatchers.<Class<?>>is(Sample.class));
assertThat(describe(second).prepare(Sample.class).load().annotationType(), CoreMatchers.<Class<?>>is(Sample.class));
}
@Test(expected = IllegalArgumentException.class)
public void testIllegalPreparation() throws Exception {
describe(first).prepare(Other.class);
}
@Test
public void testValues() throws Exception {
assertValue(first, "booleanValue", BOOLEAN, BOOLEAN);
assertValue(second, "booleanValue", BOOLEAN, BOOLEAN);
assertValue(first, "byteValue", BYTE, BYTE);
assertValue(second, "byteValue", BYTE, BYTE);
assertValue(first, "shortValue", SHORT, SHORT);
assertValue(second, "shortValue", SHORT, SHORT);
assertValue(first, "charValue", CHARACTER, CHARACTER);
assertValue(second, "charValue", CHARACTER, CHARACTER);
assertValue(first, "intValue", INTEGER, INTEGER);
assertValue(second, "intValue", INTEGER, INTEGER);
assertValue(first, "longValue", LONG, LONG);
assertValue(second, "longValue", LONG, LONG);
assertValue(first, "floatValue", FLOAT, FLOAT);
assertValue(second, "floatValue", FLOAT, FLOAT);
assertValue(first, "doubleValue", DOUBLE, DOUBLE);
assertValue(second, "doubleValue", DOUBLE, DOUBLE);
assertValue(first, "stringValue", FOO, FOO);
assertValue(second, "stringValue", BAR, BAR);
assertValue(first, "classValue", new TypeDescription.ForLoadedType(CLASS), CLASS);
assertValue(second, "classValue", new TypeDescription.ForLoadedType(CLASS), CLASS);
assertValue(first, "arrayClassValue", new TypeDescription.ForLoadedType(ARRAY_CLASS), ARRAY_CLASS);
assertValue(second, "arrayClassValue", new TypeDescription.ForLoadedType(ARRAY_CLASS), ARRAY_CLASS);
assertValue(first, "enumValue", new EnumerationDescription.ForLoadedEnumeration(ENUMERATION), ENUMERATION);
assertValue(second, "enumValue", new EnumerationDescription.ForLoadedEnumeration(ENUMERATION), ENUMERATION);
assertValue(first, "annotationValue", AnnotationDescription.ForLoadedAnnotation.of(ANNOTATION), ANNOTATION);
assertValue(second, "annotationValue", AnnotationDescription.ForLoadedAnnotation.of(ANNOTATION), ANNOTATION);
assertValue(first, "booleanArrayValue", BOOLEAN_ARRAY, BOOLEAN_ARRAY);
assertValue(second, "booleanArrayValue", BOOLEAN_ARRAY, BOOLEAN_ARRAY);
assertValue(first, "byteArrayValue", BYTE_ARRAY, BYTE_ARRAY);
assertValue(second, "byteArrayValue", BYTE_ARRAY, BYTE_ARRAY);
assertValue(first, "shortArrayValue", SHORT_ARRAY, SHORT_ARRAY);
assertValue(second, "shortArrayValue", SHORT_ARRAY, SHORT_ARRAY);
assertValue(first, "charArrayValue", CHARACTER_ARRAY, CHARACTER_ARRAY);
assertValue(second, "charArrayValue", CHARACTER_ARRAY, CHARACTER_ARRAY);
assertValue(first, "intArrayValue", INTEGER_ARRAY, INTEGER_ARRAY);
assertValue(second, "intArrayValue", INTEGER_ARRAY, INTEGER_ARRAY);
assertValue(first, "longArrayValue", LONG_ARRAY, LONG_ARRAY);
assertValue(second, "longArrayValue", LONG_ARRAY, LONG_ARRAY);
assertValue(first, "floatArrayValue", FLOAT_ARRAY, FLOAT_ARRAY);
assertValue(second, "floatArrayValue", FLOAT_ARRAY, FLOAT_ARRAY);
assertValue(first, "doubleArrayValue", DOUBLE_ARRAY, DOUBLE_ARRAY);
assertValue(second, "doubleArrayValue", DOUBLE_ARRAY, DOUBLE_ARRAY);
assertValue(first, "stringArrayValue", STRING_ARRAY, STRING_ARRAY);
assertValue(second, "stringArrayValue", STRING_ARRAY, STRING_ARRAY);
assertValue(first, "classArrayValue", new TypeDescription[]{new TypeDescription.ForLoadedType(CLASS)}, CLASS_ARRAY);
assertValue(second, "classArrayValue", new TypeDescription[]{new TypeDescription.ForLoadedType(CLASS)}, CLASS_ARRAY);
assertValue(first, "enumArrayValue", new EnumerationDescription[]{new EnumerationDescription.ForLoadedEnumeration(ENUMERATION)}, ENUMERATION_ARRAY);
assertValue(second, "enumArrayValue", new EnumerationDescription[]{new EnumerationDescription.ForLoadedEnumeration(ENUMERATION)}, ENUMERATION_ARRAY);
assertValue(first, "annotationArrayValue", new AnnotationDescription[]{AnnotationDescription.ForLoadedAnnotation.of(ANNOTATION)}, ANNOTATION_ARRAY);
assertValue(second, "annotationArrayValue", new AnnotationDescription[]{AnnotationDescription.ForLoadedAnnotation.of(ANNOTATION)}, ANNOTATION_ARRAY);
}
@Test
public void testValuesDefaults() throws Exception {
assertValue(defaultFirst, "booleanValue", BOOLEAN, BOOLEAN);
assertValue(defaultSecond, "booleanValue", OTHER_BOOLEAN, OTHER_BOOLEAN);
assertValue(defaultFirst, "byteValue", BYTE, BYTE);
assertValue(defaultSecond, "byteValue", OTHER_BYTE, OTHER_BYTE);
assertValue(defaultFirst, "shortValue", SHORT, SHORT);
assertValue(defaultSecond, "shortValue", OTHER_SHORT, OTHER_SHORT);
assertValue(defaultFirst, "charValue", CHARACTER, CHARACTER);
assertValue(defaultSecond, "charValue", OTHER_CHARACTER, OTHER_CHARACTER);
assertValue(defaultFirst, "intValue", INTEGER, INTEGER);
assertValue(defaultSecond, "intValue", OTHER_INTEGER, OTHER_INTEGER);
assertValue(defaultFirst, "longValue", LONG, LONG);
assertValue(defaultSecond, "longValue", OTHER_LONG, OTHER_LONG);
assertValue(defaultFirst, "floatValue", FLOAT, FLOAT);
assertValue(defaultSecond, "floatValue", OTHER_FLOAT, OTHER_FLOAT);
assertValue(defaultFirst, "doubleValue", DOUBLE, DOUBLE);
assertValue(defaultSecond, "doubleValue", OTHER_DOUBLE, OTHER_DOUBLE);
assertValue(defaultFirst, "stringValue", FOO, FOO);
assertValue(defaultSecond, "stringValue", BAR, BAR);
assertValue(defaultFirst, "classValue", new TypeDescription.ForLoadedType(CLASS), CLASS);
assertValue(defaultSecond, "classValue", new TypeDescription.ForLoadedType(OTHER_CLASS), OTHER_CLASS);
assertValue(defaultFirst, "arrayClassValue", new TypeDescription.ForLoadedType(ARRAY_CLASS), ARRAY_CLASS);
assertValue(defaultSecond, "arrayClassValue", new TypeDescription.ForLoadedType(OTHER_ARRAY_CLASS), OTHER_ARRAY_CLASS);
assertValue(defaultFirst, "enumValue", new EnumerationDescription.ForLoadedEnumeration(ENUMERATION), ENUMERATION);
assertValue(defaultSecond, "enumValue", new EnumerationDescription.ForLoadedEnumeration(OTHER_ENUMERATION), OTHER_ENUMERATION);
assertValue(defaultFirst, "annotationValue", AnnotationDescription.ForLoadedAnnotation.of(ANNOTATION), ANNOTATION);
assertValue(defaultSecond, "annotationValue", AnnotationDescription.ForLoadedAnnotation.of(OTHER_ANNOTATION), OTHER_ANNOTATION);
assertValue(defaultFirst, "booleanArrayValue", BOOLEAN_ARRAY, BOOLEAN_ARRAY);
assertValue(defaultSecond, "booleanArrayValue", OTHER_BOOLEAN_ARRAY, OTHER_BOOLEAN_ARRAY);
assertValue(defaultFirst, "byteArrayValue", BYTE_ARRAY, BYTE_ARRAY);
assertValue(defaultSecond, "byteArrayValue", OTHER_BYTE_ARRAY, OTHER_BYTE_ARRAY);
assertValue(defaultFirst, "shortArrayValue", SHORT_ARRAY, SHORT_ARRAY);
assertValue(defaultSecond, "shortArrayValue", OTHER_SHORT_ARRAY, OTHER_SHORT_ARRAY);
assertValue(defaultFirst, "charArrayValue", CHARACTER_ARRAY, CHARACTER_ARRAY);
assertValue(defaultSecond, "charArrayValue", OTHER_CHARACTER_ARRAY, OTHER_CHARACTER_ARRAY);
assertValue(defaultFirst, "intArrayValue", INTEGER_ARRAY, INTEGER_ARRAY);
assertValue(defaultSecond, "intArrayValue", OTHER_INTEGER_ARRAY, OTHER_INTEGER_ARRAY);
assertValue(defaultFirst, "longArrayValue", LONG_ARRAY, LONG_ARRAY);
assertValue(defaultSecond, "longArrayValue", OTHER_LONG_ARRAY, OTHER_LONG_ARRAY);
assertValue(defaultFirst, "floatArrayValue", FLOAT_ARRAY, FLOAT_ARRAY);
assertValue(defaultSecond, "floatArrayValue", OTHER_FLOAT_ARRAY, OTHER_FLOAT_ARRAY);
assertValue(defaultFirst, "doubleArrayValue", DOUBLE_ARRAY, DOUBLE_ARRAY);
assertValue(defaultSecond, "doubleArrayValue", OTHER_DOUBLE_ARRAY, OTHER_DOUBLE_ARRAY);
assertValue(defaultFirst, "stringArrayValue", STRING_ARRAY, STRING_ARRAY);
assertValue(defaultSecond, "stringArrayValue", OTHER_STRING_ARRAY, OTHER_STRING_ARRAY);
assertValue(defaultFirst, "classArrayValue", new TypeDescription[]{new TypeDescription.ForLoadedType(CLASS)}, CLASS_ARRAY);
assertValue(defaultSecond, "classArrayValue", new TypeDescription[]{new TypeDescription.ForLoadedType(OTHER_CLASS)}, OTHER_CLASS_ARRAY);
assertValue(defaultFirst, "enumArrayValue", new EnumerationDescription[]{new EnumerationDescription.ForLoadedEnumeration(ENUMERATION)}, ENUMERATION_ARRAY);
assertValue(defaultSecond, "enumArrayValue", new EnumerationDescription[]{new EnumerationDescription.ForLoadedEnumeration(OTHER_ENUMERATION)}, OTHER_ENUMERATION_ARRAY);
assertValue(defaultFirst, "annotationArrayValue", new AnnotationDescription[]{AnnotationDescription.ForLoadedAnnotation.of(ANNOTATION)}, ANNOTATION_ARRAY);
assertValue(defaultSecond, "annotationArrayValue", new AnnotationDescription[]{AnnotationDescription.ForLoadedAnnotation.of(OTHER_ANNOTATION)}, OTHER_ANNOTATION_ARRAY);
}
@Test
public void testRetention() throws Exception {
assertThat(describe(first).getRetention(), is(RetentionPolicy.RUNTIME));
}
@Test
public void testAnnotationTarget() throws Exception {
assertThat(describe(first).getElementTypes(), is((Set<ElementType>) new HashSet<ElementType>(Arrays.asList(ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD,
ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE))));
assertThat(describe(explicitTarget).getElementTypes(), is(Collections.singleton(ElementType.TYPE)));
}
@Test
public void testInheritance() throws Exception {
assertThat(describe(first).isInherited(), is(false));
assertThat(describe(defaultFirst).isInherited(), is(true));
}
@Test
public void testDocumented() throws Exception {
assertThat(describe(first).isDocumented(), is(false));
assertThat(describe(defaultFirst).isDocumented(), is(true));
}
private void assertValue(Annotation annotation, String methodName, Object unloadedValue, Object loadedValue) throws Exception {
assertThat(describe(annotation).getValue(new MethodDescription.ForLoadedMethod(annotation.annotationType().getDeclaredMethod(methodName))).resolve(),
is(unloadedValue));
assertThat(describe(annotation).getValue(new MethodDescription.Latent(new TypeDescription.ForLoadedType(annotation.annotationType()),
methodName,
Opcodes.ACC_PUBLIC,
Collections.<TypeVariableToken>emptyList(),
new TypeDescription.Generic.OfNonGenericType.ForLoadedType(annotation.annotationType().getDeclaredMethod(methodName).getReturnType()),
Collections.<ParameterDescription.Token>emptyList(),
Collections.<TypeDescription.Generic>emptyList(),
Collections.<AnnotationDescription>emptyList(),
AnnotationValue.UNDEFINED,
TypeDescription.Generic.UNDEFINED)).resolve(), is(unloadedValue));
assertThat(annotation.annotationType().getDeclaredMethod(methodName).invoke(describe(annotation).prepare(annotation.annotationType()).load()),
is(loadedValue));
}
public enum SampleEnumeration {
VALUE,
OTHER
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Sample2 {
Class<?> foo();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Sample {
boolean booleanValue();
byte byteValue();
short shortValue();
char charValue();
int intValue();
long longValue();
float floatValue();
double doubleValue();
String stringValue();
Class<?> classValue();
Class<?> arrayClassValue();
SampleEnumeration enumValue();
Other annotationValue();
boolean[] booleanArrayValue();
byte[] byteArrayValue();
short[] shortArrayValue();
char[] charArrayValue();
int[] intArrayValue();
long[] longArrayValue();
float[] floatArrayValue();
double[] doubleArrayValue();
String[] stringArrayValue();
Class<?>[] classArrayValue();
SampleEnumeration[] enumArrayValue();
Other[] annotationArrayValue();
}
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface SampleDefault {
boolean booleanValue() default BOOLEAN;
byte byteValue() default BYTE;
short shortValue() default SHORT;
char charValue() default CHARACTER;
int intValue() default INTEGER;
long longValue() default LONG;
float floatValue() default FLOAT;
double doubleValue() default DOUBLE;
String stringValue() default FOO;
Class<?> classValue() default Void.class;
Class<?> arrayClassValue() default Void[].class;
SampleEnumeration enumValue() default SampleEnumeration.VALUE;
Other annotationValue() default @Other;
boolean[] booleanArrayValue() default BOOLEAN;
byte[] byteArrayValue() default BYTE;
short[] shortArrayValue() default SHORT;
char[] charArrayValue() default CHARACTER;
int[] intArrayValue() default INTEGER;
long[] longArrayValue() default LONG;
float[] floatArrayValue() default FLOAT;
double[] doubleArrayValue() default DOUBLE;
String[] stringArrayValue() default FOO;
Class<?>[] classArrayValue() default Void.class;
SampleEnumeration[] enumArrayValue() default SampleEnumeration.VALUE;
Other[] annotationArrayValue() default @Other;
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Other {
String value() default FOO;
}
@Sample(booleanValue = BOOLEAN,
byteValue = BYTE,
charValue = CHARACTER,
shortValue = SHORT,
intValue = INTEGER,
longValue = LONG,
floatValue = FLOAT,
doubleValue = DOUBLE,
stringValue = FOO,
classValue = Void.class,
arrayClassValue = Void[].class,
enumValue = SampleEnumeration.VALUE,
annotationValue = @Other,
booleanArrayValue = BOOLEAN,
byteArrayValue = BYTE,
shortArrayValue = SHORT,
charArrayValue = CHARACTER,
intArrayValue = INTEGER,
longArrayValue = LONG,
floatArrayValue = FLOAT,
doubleArrayValue = DOUBLE,
stringArrayValue = FOO,
classArrayValue = Void.class,
enumArrayValue = SampleEnumeration.VALUE,
annotationArrayValue = @Other)
private static class FooSample {
/* empty */
}
@Sample(booleanValue = BOOLEAN,
byteValue = BYTE,
charValue = CHARACTER,
shortValue = SHORT,
intValue = INTEGER,
longValue = LONG,
floatValue = FLOAT,
doubleValue = DOUBLE,
stringValue = BAR,
classValue = Void.class,
arrayClassValue = Void[].class,
enumValue = SampleEnumeration.VALUE,
annotationValue = @Other,
booleanArrayValue = BOOLEAN,
byteArrayValue = BYTE,
shortArrayValue = SHORT,
charArrayValue = CHARACTER,
intArrayValue = INTEGER,
longArrayValue = LONG,
floatArrayValue = FLOAT,
doubleArrayValue = DOUBLE,
stringArrayValue = FOO,
classArrayValue = Void.class,
enumArrayValue = SampleEnumeration.VALUE,
annotationArrayValue = @Other)
private static class BarSample {
/* empty */
}
@SampleDefault
private static class DefaultSample {
/* empty */
}
@SampleDefault(booleanValue = !BOOLEAN,
byteValue = BYTE * 2,
charValue = CHARACTER * 2,
shortValue = SHORT * 2,
intValue = INTEGER * 2,
longValue = LONG * 2,
floatValue = FLOAT * 2,
doubleValue = DOUBLE * 2,
stringValue = BAR,
classValue = Object.class,
arrayClassValue = Object[].class,
enumValue = SampleEnumeration.OTHER,
annotationValue = @Other(BAR),
booleanArrayValue = !BOOLEAN,
byteArrayValue = OTHER_BYTE,
shortArrayValue = OTHER_SHORT,
charArrayValue = OTHER_CHARACTER,
intArrayValue = OTHER_INTEGER,
longArrayValue = OTHER_LONG,
floatArrayValue = OTHER_FLOAT,
doubleArrayValue = OTHER_DOUBLE,
stringArrayValue = BAR,
classArrayValue = Object.class,
enumArrayValue = SampleEnumeration.OTHER,
annotationArrayValue = @Other(BAR))
private static class NonDefaultSample {
/* empty */
}
@Other
private static class EnumerationCarrier {
/* empty */
}
@Other(BAR)
private static class OtherEnumerationCarrier {
/* empty */
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
protected @interface ExplicitTarget {
@ExplicitTarget
class Carrier {
/* empty */
}
}
@Retention(RetentionPolicy.RUNTIME)
public @interface BrokenAnnotation {
String stringValue();
SampleEnumeration enumValue();
Class<?> classValue();
}
private static class AnnotationValueBreaker extends AsmVisitorWrapper.AbstractBase {
@Override
public ClassVisitor wrap(TypeDescription instrumentedType,
ClassVisitor classVisitor,
Implementation.Context implementationContext,
TypePool typePool,
FieldList<FieldDescription.InDefinedShape> fields,
MethodList<?> methods,
int writerFlags,
int readerFlags) {
return new BreakingClassVisitor(classVisitor);
}
private static class BreakingClassVisitor extends ClassVisitor {
public BreakingClassVisitor(ClassVisitor classVisitor) {
super(Opcodes.ASM5, classVisitor);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
AnnotationVisitor annotationVisitor = visitAnnotation(Type.getDescriptor(BrokenAnnotation.class), true);
annotationVisitor.visit("stringValue", INTEGER);
annotationVisitor.visitEnum("enumValue", Type.getDescriptor(SampleEnumeration.class), FOO);
annotationVisitor.visit("classValue", Type.getType("Lnet/bytebuddy/inexistant/Foo;"));
annotationVisitor.visitEnd();
}
}
}
}