Allow for recursive annotations (#97)

Co-authored-by: Mariell <proximyst@proximyst.com>
This commit is contained in:
Alexander Söderberg 2020-10-24 19:28:56 +02:00
parent e26d01388d
commit a68bc0bea7
3 changed files with 99 additions and 6 deletions

View file

@ -51,11 +51,13 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
@ -106,16 +108,40 @@ public final class AnnotationParser<C> {
));
}
@SuppressWarnings("unchecked")
static <A extends Annotation> @Nullable A getAnnotationRecursively(
final @NonNull Annotation[] annotations,
final @NonNull Class<A> clazz,
final @NonNull Set<Class<? extends Annotation>> checkedAnnotations
) {
A innerCandidate = null;
for (final Annotation annotation : annotations) {
if (!checkedAnnotations.add(annotation.annotationType())) {
continue;
}
if (annotation.annotationType().equals(clazz)) {
return (A) annotation;
}
if (annotation.annotationType().getPackage().getName().startsWith("java.lang")) {
continue;
}
final A inner = getAnnotationRecursively(annotation.annotationType().getAnnotations(), clazz, checkedAnnotations);
if (inner != null) {
innerCandidate = inner;
}
}
return innerCandidate;
}
static <A extends Annotation> @Nullable A getMethodOrClassAnnotation(
final @NonNull Method method,
final @NonNull Class<A> clazz
) {
if (method.isAnnotationPresent(clazz)) {
return method.getAnnotation(clazz);
} else if (method.getDeclaringClass().isAnnotationPresent(clazz)) {
return method.getDeclaringClass().getAnnotation(clazz);
A annotation = getAnnotationRecursively(method.getAnnotations(), clazz, new HashSet<>());
if (annotation == null) {
annotation = getAnnotationRecursively(method.getDeclaringClass().getAnnotations(), clazz, new HashSet<>());
}
return null;
return annotation;
}
static <A extends Annotation> boolean methodOrClassHasAnnotation(

View file

@ -32,6 +32,11 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@ -76,6 +81,34 @@ class AnnotationParserTest {
Assertions.assertEquals(NAMED_SUGGESTIONS, manager.suggest(new TestCommandSender(), "namedsuggestions "));
}
@Test
void testAnnotationResolver() throws Exception {
final Class<AnnotatedClass> annotatedClass = AnnotatedClass.class;
final Method annotatedMethod = annotatedClass.getDeclaredMethod("annotatedMethod");
System.out.println("Looking for @CommandDescription");
final CommandDescription commandDescription = AnnotationParser.getMethodOrClassAnnotation(annotatedMethod,
CommandDescription.class);
Assertions.assertNotNull(commandDescription);
Assertions.assertEquals("Hello World!", commandDescription.value());
System.out.println("Looking for @CommandPermission");
final CommandPermission commandPermission = AnnotationParser.getMethodOrClassAnnotation(annotatedMethod,
CommandPermission.class);
Assertions.assertNotNull(commandPermission);
Assertions.assertEquals("some.permission", commandPermission.value());
System.out.println("Looking for @CommandMethod");
final CommandMethod commandMethod = AnnotationParser.getMethodOrClassAnnotation(annotatedMethod,
CommandMethod.class);
Assertions.assertNotNull(commandMethod);
Assertions.assertEquals("method", commandMethod.value());
System.out.println("Looking for @Regex");
@SuppressWarnings("unused")
final Regex regex = AnnotationParser.getMethodOrClassAnnotation(annotatedMethod, Regex.class);
}
@ProxiedBy("proxycommand")
@CommandMethod("test|t literal <int> [string]")
public void testCommand(
@ -103,4 +136,37 @@ class AnnotationParserTest {
) {
}
@CommandPermission("some.permission")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
private @interface AnnotatedAnnotation {
}
@Bad1
@CommandDescription("Hello World!")
private static class AnnotatedClass {
@CommandMethod("method")
@AnnotatedAnnotation
public static void annotatedMethod() {
}
}
@Bad2
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
private @interface Bad1 {
}
@Bad1
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
private @interface Bad2 {
}
}