1313 */
1414package ch .qos .logback .core .joran .spi ;
1515
16+
17+ import java .lang .annotation .Annotation ;
18+ import java .util .HashSet ;
19+ import java .util .Set ;
1620import ch .qos .logback .core .spi .LifeCycle ;
1721
1822public class NoAutoStartUtil {
@@ -24,13 +28,83 @@ public class NoAutoStartUtil {
2428 * @param o
2529 * @return true for classes not marked with the NoAutoStart annotation
2630 */
27- static public boolean notMarkedWithNoAutoStart (Object o ) {
31+ public static boolean notMarkedWithNoAutoStart (Object o ) {
32+ if (o == null ) {
33+ return false ;
34+ }
2835 Class <?> clazz = o .getClass ();
29- NoAutoStart a = clazz . getAnnotation ( NoAutoStart .class );
36+ NoAutoStart a = findAnnotation ( clazz , NoAutoStart .class );
3037 return a == null ;
3138 }
3239
33- /**
40+ /**
41+ * Find a single {@link Annotation} of {@code annotationType} on the
42+ * supplied {@link Class}, traversing its interfaces, annotations, and
43+ * superclasses if the annotation is not <em>directly present</em> on
44+ * the given class itself.
45+ * <p>This method explicitly handles class-level annotations which are not
46+ * declared as {@link java.lang.annotation.Inherited inherited} <em>as well
47+ * as meta-annotations and annotations on interfaces</em>.
48+ * <p>The algorithm operates as follows:
49+ * <ol>
50+ * <li>Search for the annotation on the given class and return it if found.
51+ * <li>Recursively search through all annotations that the given class declares.
52+ * <li>Recursively search through all interfaces that the given class declares.
53+ * <li>Recursively search through the superclass hierarchy of the given class.
54+ * </ol>
55+ * <p>Note: in this context, the term <em>recursively</em> means that the search
56+ * process continues by returning to step #1 with the current interface,
57+ * annotation, or superclass as the class to look for annotations on.
58+ * @param clazz the class to look for annotations on
59+ * @param annotationType the type of annotation to look for
60+ * @return the first matching annotation, or {@code null} if not found
61+ */
62+ private static <A extends Annotation > A findAnnotation (Class <?> clazz , Class <A > annotationType ) {
63+ return findAnnotation (clazz , annotationType , new HashSet <>());
64+ }
65+
66+ /**
67+ * Perform the search algorithm for {@link #findAnnotation(Class, Class)},
68+ * avoiding endless recursion by tracking which annotations have already
69+ * been <em>visited</em>.
70+ * @param clazz the class to look for annotations on
71+ * @param annotationType the type of annotation to look for
72+ * @param visited the set of annotations that have already been visited
73+ * @return the first matching annotation, or {@code null} if not found
74+ */
75+ @ SuppressWarnings ("unchecked" )
76+ private static <A extends Annotation > A findAnnotation (Class <?> clazz , Class <A > annotationType , Set <Annotation > visited ) {
77+
78+ Annotation [] anns = clazz .getDeclaredAnnotations ();
79+ for (Annotation ann : anns ) {
80+ if (ann .annotationType () == annotationType ) {
81+ return (A ) ann ;
82+ }
83+ }
84+ for (Annotation ann : anns ) {
85+ if (visited .add (ann )) {
86+ A annotation = findAnnotation (ann .annotationType (), annotationType , visited );
87+ if (annotation != null ) {
88+ return annotation ;
89+ }
90+ }
91+ }
92+
93+ for (Class <?> ifc : clazz .getInterfaces ()) {
94+ A annotation = findAnnotation (ifc , annotationType , visited );
95+ if (annotation != null ) {
96+ return annotation ;
97+ }
98+ }
99+
100+ Class <?> superclass = clazz .getSuperclass ();
101+ if (superclass == null || Object .class == superclass ) {
102+ return null ;
103+ }
104+ return findAnnotation (superclass , annotationType , visited );
105+ }
106+
107+ /**
34108 * Is the object a {@link LifeCycle} and is it marked not marked with
35109 * the NoAutoStart annotation.
36110 * @param o
@@ -43,5 +117,4 @@ static public boolean shouldBeStarted(Object o) {
43117 } else
44118 return false ;
45119 }
46-
47120}
0 commit comments