✨ Allow for recursive annotations (#97)
Co-authored-by: Mariell <proximyst@proximyst.com>
This commit is contained in:
parent
e26d01388d
commit
a68bc0bea7
3 changed files with 99 additions and 6 deletions
|
|
@ -25,7 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Changed
|
||||
- Allow for combined presence flags, such that `-a -b -c` is equivalent to `-abc`
|
||||
- Allow for class annotations as a default for when an annotation is not present on a method.
|
||||
- Allow for class annotations as a default for when an annotation is not present on a method
|
||||
- Allow for annotated annotations
|
||||
|
||||
### Fixed
|
||||
- Fix arguments with no required children not being executors (cloud-brigadier)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue