/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.test.ConfigurationKind;
import java.lang.annotation.*;
import java.lang.reflect.*;
public class AnnotationGenTest extends CodegenTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL);
}
public void testVolatileProperty() throws Exception {
loadText("abstract class Foo { @Volatile public var x: String = \"\"; }");
Class<?> aClass = generateClass("Foo");
Field x = aClass.getDeclaredField("x");
assertTrue((x.getModifiers() & Modifier.VOLATILE) != 0);
}
public void testPropField() throws Exception {
loadText("@[java.lang.Deprecated] var x = 0");
Class<?> srcClass = generateFacadeClass();
assertNull(srcClass.getDeclaredMethod("getX").getAnnotation(Deprecated.class));
assertNull(srcClass.getDeclaredMethod("setX", int.class).getAnnotation(Deprecated.class));
assertNotNull(srcClass.getDeclaredField("x").getAnnotation(Deprecated.class));
}
public void testPropGetter() throws Exception {
loadText("var x = 0\n" +
"@[java.lang.Deprecated] get");
Class<?> srcClass = generateFacadeClass();
assertNotNull(srcClass.getDeclaredMethod("getX").getAnnotation(Deprecated.class));
assertNull(srcClass.getDeclaredMethod("setX", int.class).getAnnotation(Deprecated.class));
assertNull(srcClass.getDeclaredField("x").getAnnotation(Deprecated.class));
}
public void testPropSetter() throws Exception {
loadText("var x = 0\n" +
"@[java.lang.Deprecated] set");
Class<?> scrClass = generateFacadeClass();
assertNull(scrClass.getDeclaredMethod("getX").getAnnotation(Deprecated.class));
assertNotNull(scrClass.getDeclaredMethod("setX", int.class).getAnnotation(Deprecated.class));
assertNull(scrClass.getDeclaredField("x").getAnnotation(Deprecated.class));
}
public void testAnnotationForParamInTopLevelFunction() throws Exception {
loadText("fun x(@[java.lang.Deprecated] i: Int) {}");
Class<?> srcClass = generateFacadeClass();
Method srcClassMethod = srcClass.getMethod("x", int.class);
assertNotNull(srcClassMethod);
assertNotNull(getDeprecatedAnnotationFromList(srcClassMethod.getParameterAnnotations()[0]));
}
public void testAnnotationForParamInInstanceFunction() throws NoSuchFieldException, NoSuchMethodException {
loadText("class A() { fun x(@[java.lang.Deprecated] i: Int) {}}");
Class<?> aClass = generateClass("A");
Method x = aClass.getMethod("x", int.class);
assertNotNull(x);
// Get annotations for first parameter
Annotation[] annotations = x.getParameterAnnotations()[0];
assertNotNull(getDeprecatedAnnotationFromList(annotations));
}
public void testAnnotationForParamInInstanceExtensionFunction() throws NoSuchFieldException, NoSuchMethodException {
loadText("class A() { fun String.x(@[java.lang.Deprecated] i: Int) {}}");
Class<?> aClass = generateClass("A");
Method x = aClass.getMethod("x", String.class, int.class);
assertNotNull(x);
// Get annotations for first real parameter
Annotation[] annotations = x.getParameterAnnotations()[1];
assertNotNull(getDeprecatedAnnotationFromList(annotations));
}
public void testParamInConstructor() throws NoSuchFieldException, NoSuchMethodException {
loadText("class A (@[java.lang.Deprecated] x: Int) {}");
Class<?> aClass = generateClass("A");
Constructor constructor = aClass.getDeclaredConstructor(int.class);
assertNotNull(constructor);
// Get annotations for first parameter
Annotation[] annotations = constructor.getParameterAnnotations()[0];
assertNotNull(getDeprecatedAnnotationFromList(annotations));
}
public void testParamInEnumConstructor() throws NoSuchFieldException, NoSuchMethodException {
loadText("enum class E(@[java.lang.Deprecated] p: String)");
Class<?> klass = generateClass("E");
Constructor constructor = klass.getDeclaredConstructor(String.class, int.class, String.class);
assertNotNull(constructor);
// Get annotations for first parameter
Annotation[] annotations = constructor.getParameterAnnotations()[0];
assertNotNull(getDeprecatedAnnotationFromList(annotations));
}
public void testParamInInnerConstructor() throws NoSuchFieldException, NoSuchMethodException {
loadText("class Outer { inner class Inner(@[java.lang.Deprecated] x: Int) }");
Class<?> outer = generateClass("Outer");
Class<?> inner = outer.getDeclaredClasses()[0];
Constructor constructor = inner.getDeclaredConstructor(outer, int.class);
assertNotNull(constructor);
// Get annotations for first parameter
Annotation[] annotations = constructor.getParameterAnnotations()[0];
assertNotNull(getDeprecatedAnnotationFromList(annotations));
}
public void testPropFieldInConstructor() throws NoSuchFieldException, NoSuchMethodException {
loadText("class A (@field:java.lang.Deprecated @param:java.lang.Deprecated var x: Int) {}");
Class<?> aClass = generateClass("A");
Constructor constructor = aClass.getDeclaredConstructor(int.class);
assertNotNull(constructor);
// Get annotations for first parameter
Annotation[] annotations = constructor.getParameterAnnotations()[0];
assertNotNull(getDeprecatedAnnotationFromList(annotations));
assertNull(aClass.getDeclaredMethod("getX").getAnnotation(Deprecated.class));
assertNull(aClass.getDeclaredMethod("setX", int.class).getAnnotation(Deprecated.class));
assertNotNull(aClass.getDeclaredField("x").getAnnotation(Deprecated.class));
}
public void testAnnotationWithParamForParamInFunction() throws Exception {
loadText("import java.lang.annotation.*\n" +
"@java.lang.annotation.Retention(RetentionPolicy.RUNTIME) annotation class A(val a: String)\n" +
"fun x(@A(\"239\") i: Int) {}");
Class<?> packageClass = generateFacadeClass();
Method packageClassMethod = packageClass.getMethod("x", int.class);
assertNotNull(packageClassMethod);
assertNotNull(getAnnotationByName(packageClassMethod.getParameterAnnotations()[0], "A"));
}
@Nullable
private static Annotation getAnnotationByName(@NotNull Annotation[] annotations, @NotNull String name) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().getCanonicalName().equals(name)) {
return annotation;
}
}
return null;
}
private static Deprecated getDeprecatedAnnotationFromList(Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation instanceof Deprecated) {
return (Deprecated) annotation;
}
}
return null;
}
public void testConstructor() throws NoSuchFieldException, NoSuchMethodException {
loadText("class A @[java.lang.Deprecated] constructor() {}");
Class<?> aClass = generateClass("A");
Constructor x = aClass.getDeclaredConstructor();
Deprecated annotation = (Deprecated) x.getAnnotation(Deprecated.class);
assertNotNull(annotation);
}
public void testMethod() throws Exception {
loadText("@[java.lang.Deprecated] fun x () {}");
Class<?> srcClass = generateFacadeClass();
Method srcClassMethod = srcClass.getDeclaredMethod("x");
assertNotNull(srcClassMethod.getAnnotation(Deprecated.class));
}
public void testClass() throws NoSuchFieldException, NoSuchMethodException {
loadText("@[java.lang.Deprecated] class A () {}");
Class aClass = generateClass("A");
Deprecated annotation = (Deprecated) aClass.getAnnotation(Deprecated.class);
assertNotNull(annotation);
}
public void testSimplestAnnotationClass() {
loadText("annotation class A");
Class<?> aClass = generateClass("A");
Class[] interfaces = aClass.getInterfaces();
assertEquals(0, aClass.getDeclaredMethods().length);
assertTrue(aClass.isAnnotation());
assertEquals(1, interfaces.length);
assertEquals("java.lang.annotation.Annotation", interfaces[0].getName());
}
public void testAnnotationClassWithStringProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"" +
"@java.lang.annotation.Retention(RetentionPolicy.RUNTIME) annotation class A(val a: String)\n" +
"" +
"@A(\"239\") class B()");
Class aClass = generateClass("A");
Retention annotation = (Retention)aClass.getAnnotation(Retention.class);
RetentionPolicy value = annotation.value();
assertEquals(RetentionPolicy.RUNTIME, value);
Method[] methods = aClass.getDeclaredMethods();
assertEquals(1, methods.length);
assertEquals("a", methods[0].getName());
assertEquals(String.class, methods[0].getReturnType());
assertEquals(0, methods[0].getParameterTypes().length);
assertTrue(aClass.isAnnotation());
Class<?> bClass = aClass.getClassLoader().loadClass("B");
Annotation bClassAnnotation = bClass.getAnnotation(aClass);
assertNotNull(bClassAnnotation);
assertEquals("239", methods[0].invoke(bClassAnnotation));
}
public void testAnnotationClassWithAnnotationProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"" +
"annotation class C(val c: String)\n" +
"@java.lang.annotation.Retention(RetentionPolicy.RUNTIME) annotation class A(val a: C)\n" +
"" +
"@A(C(\"239\")) class B()");
Class aClass = generateClass("A");
Retention annotation = (Retention)aClass.getAnnotation(Retention.class);
RetentionPolicy value = annotation.value();
assertEquals(RetentionPolicy.RUNTIME, value);
Method[] methods = aClass.getDeclaredMethods();
assertEquals(1, methods.length);
assertEquals("a", methods[0].getName());
assertEquals("C", methods[0].getReturnType().getName());
assertEquals(0, methods[0].getParameterTypes().length);
assertTrue(aClass.isAnnotation());
Class<?> bClass = aClass.getClassLoader().loadClass("B");
Annotation bClassAnnotation = bClass.getAnnotation(aClass);
assertNotNull(bClassAnnotation);
Object invoke = methods[0].invoke(bClassAnnotation);
// there is some Proxy here
Class<?> cClass = invoke.getClass().getInterfaces()[0];
assertEquals("C", cClass.getName());
assertEquals("239", cClass.getDeclaredMethod("c").invoke(invoke));
}
public void testAnnotationClassWithStringArrayProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"" +
"@java.lang.annotation.Retention(RetentionPolicy.RUNTIME) annotation class A(val a: Array<String>)\n" +
"" +
"@A(arrayOf(\"239\",\"932\")) class B()");
Class aClass = generateClass("A");
Retention annotation = (Retention)aClass.getAnnotation(Retention.class);
RetentionPolicy value = annotation.value();
assertEquals(RetentionPolicy.RUNTIME, value);
Method[] methods = aClass.getDeclaredMethods();
assertEquals(1, methods.length);
assertEquals("a", methods[0].getName());
assertEquals(String[].class, methods[0].getReturnType());
assertEquals(0, methods[0].getParameterTypes().length);
assertTrue(aClass.isAnnotation());
Class<?> bClass = aClass.getClassLoader().loadClass("B");
Annotation bClassAnnotation = bClass.getAnnotation(aClass);
assertNotNull(bClassAnnotation);
Object invoke = methods[0].invoke(bClassAnnotation);
assertEquals("239", ((String[])invoke)[0]);
assertEquals("932", ((String[])invoke)[1]);
}
public void testAnnotationClassWithIntArrayProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"" +
"@java.lang.annotation.Retention(RetentionPolicy.RUNTIME) annotation class A(val a: IntArray)\n" +
"" +
"@A(intArrayOf(239,932)) class B()");
Class aClass = generateClass("A");
Retention annotation = (Retention)aClass.getAnnotation(Retention.class);
RetentionPolicy value = annotation.value();
assertEquals(RetentionPolicy.RUNTIME, value);
Method[] methods = aClass.getDeclaredMethods();
assertEquals(1, methods.length);
assertEquals("a", methods[0].getName());
assertEquals(int[].class, methods[0].getReturnType());
assertEquals(0, methods[0].getParameterTypes().length);
assertTrue(aClass.isAnnotation());
Class<?> bClass = aClass.getClassLoader().loadClass("B");
Annotation bClassAnnotation = bClass.getAnnotation(aClass);
assertNotNull(bClassAnnotation);
Object invoke = methods[0].invoke(bClassAnnotation);
assertEquals(239, ((int[])invoke)[0]);
assertEquals(932, ((int[])invoke)[1]);
}
public void testAnnotationClassWithEnumArrayProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"" +
"@java.lang.annotation.Target(ElementType.TYPE, ElementType.METHOD) annotation class A");
Class aClass = generateClass("A");
Target annotation = (Target)aClass.getAnnotation(Target.class);
ElementType[] value = annotation.value();
assertEquals(2, value.length);
assertEquals(ElementType.TYPE, value[0]);
assertEquals(ElementType.METHOD, value[1]);
}
public void testAnnotationClassWithAnnotationArrayProperty()
throws
NoSuchFieldException,
NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InstantiationException,
InvocationTargetException {
loadText("import java.lang.annotation.*\n" +
"import java.lang.annotation.Retention\n" +
"" +
"@Retention(RetentionPolicy.RUNTIME) annotation class A(val a: Array<Retention>)\n" +
"" +
"@A(arrayOf(Retention(RetentionPolicy.RUNTIME),Retention(RetentionPolicy.SOURCE))) class B()");
Class aClass = generateClass("A");
Method[] methods = aClass.getDeclaredMethods();
assertEquals(1, methods.length);
assertEquals("a", methods[0].getName());
Class<?> bClass = aClass.getClassLoader().loadClass("B");
Annotation bClassAnnotation = bClass.getAnnotation(aClass);
assertNotNull(bClassAnnotation);
Object invoke = methods[0].invoke(bClassAnnotation);
Retention[] invoke1 = (Retention[])invoke;
assertEquals(2, invoke1.length);
assertEquals(invoke1[0].value(), RetentionPolicy.RUNTIME);
assertEquals(invoke1[1].value(), RetentionPolicy.SOURCE);
}
}