Skip to content

Commit

Permalink
Fix bug in TYPE_HIERARCHY_AND_ENCLOSING_CLASSES strategy
Browse files Browse the repository at this point in the history
Prior to this commit, the new `TYPE_HIERARCHY_AND_ENCLOSING_CLASSES`
annotation search strategy failed to find annotations on enclosing
classes if the source class was a nested class that itself had no
annotations present.

This commit fixes this by adding special logic to AnnotationsScanner's
isWithoutHierarchy() method to properly support nested classes.

Closes gh-23378
  • Loading branch information
sbrannen committed Jul 31, 2019
1 parent b03dd47 commit de3c115
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
* {@link AnnotatedElement}.
*
* @author Phillip Webb
* @author Sam Brannen
* @since 5.2
* @see AnnotationsProcessor
*/
Expand Down Expand Up @@ -121,7 +122,7 @@ private static <C, R> R processClass(C context, Class<?> source,
case DIRECT:
return processElement(context, source, processor, classFilter);
case INHERITED_ANNOTATIONS:
return processClassInheritedAnnotations(context, source, processor, classFilter);
return processClassInheritedAnnotations(context, source, searchStrategy, processor, classFilter);
case SUPERCLASS:
return processClassHierarchy(context, source, processor, classFilter, false, false);
case EXHAUSTIVE:
Expand All @@ -135,9 +136,9 @@ private static <C, R> R processClass(C context, Class<?> source,

@Nullable
private static <C, R> R processClassInheritedAnnotations(C context, Class<?> source,
AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {
SearchStrategy searchStrategy, AnnotationsProcessor<C, R> processor, @Nullable BiPredicate<C, Class<?>> classFilter) {

if (isWithoutHierarchy(source)) {
if (isWithoutHierarchy(source, searchStrategy)) {
return processElement(context, source, processor, classFilter);
}
Annotation[] relevant = null;
Expand Down Expand Up @@ -505,7 +506,7 @@ static boolean isKnownEmpty(AnnotatedElement source, SearchStrategy searchStrate
if (hasPlainJavaAnnotationsOnly(source)) {
return true;
}
if (searchStrategy == SearchStrategy.DIRECT || isWithoutHierarchy(source)) {
if (searchStrategy == SearchStrategy.DIRECT || isWithoutHierarchy(source, searchStrategy)) {
if (source instanceof Method && ((Method) source).isBridge()) {
return false;
}
Expand All @@ -530,19 +531,21 @@ static boolean hasPlainJavaAnnotationsOnly(Class<?> type) {
return (type.getName().startsWith("java.") || type == Ordered.class);
}

private static boolean isWithoutHierarchy(AnnotatedElement source) {
private static boolean isWithoutHierarchy(AnnotatedElement source, SearchStrategy searchStrategy) {
if (source == Object.class) {
return true;
}
if (source instanceof Class) {
Class<?> sourceClass = (Class<?>) source;
return (sourceClass.getSuperclass() == Object.class &&
boolean noSuperTypes = (sourceClass.getSuperclass() == Object.class &&
sourceClass.getInterfaces().length == 0);
return (searchStrategy == SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES ? noSuperTypes &&
sourceClass.getEnclosingClass() == null : noSuperTypes);
}
if (source instanceof Method) {
Method sourceMethod = (Method) source;
return (Modifier.isPrivate(sourceMethod.getModifiers()) ||
isWithoutHierarchy(sourceMethod.getDeclaringClass()));
isWithoutHierarchy(sourceMethod.getDeclaringClass(), searchStrategy));
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,19 @@ public void streamTypeHierarchyFromClassWithInterface() throws Exception {
Transactional.class)).hasSize(1);
}

@Test
public void streamTypeHierarchyAndEnclosingClassesFromNonAnnotatedInnerClassWithAnnotatedEnclosingClass() {
Stream<Class<?>> classes = MergedAnnotations.from(AnnotatedClass.NonAnnotatedInnerClass.class,
SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES).stream().map(MergedAnnotation::getType);
assertThat(classes).containsExactly(Component.class, Indexed.class);
}

@Test
public void streamTypeHierarchyAndEnclosingClassesFromNonAnnotatedStaticNestedClassWithAnnotatedEnclosingClass() {
Stream<Class<?>> classes = MergedAnnotations.from(AnnotatedClass.NonAnnotatedStaticNestedClass.class,
SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES).stream().map(MergedAnnotation::getType);
assertThat(classes).containsExactly(Component.class, Indexed.class);
}
@Test
public void getFromMethodWithMethodAnnotationOnLeaf() throws Exception {
Method method = Leaf.class.getMethod("annotatedOnLeaf");
Expand Down Expand Up @@ -2112,6 +2125,16 @@ public void asAnnotationAttributesReturnsPopulatedAnnotationAttributes() {
static class NonAnnotatedClass {
}

@Component
static class AnnotatedClass {

class NonAnnotatedInnerClass {
}

static class NonAnnotatedStaticNestedClass {
}
}

static interface NonAnnotatedInterface {
}

Expand Down Expand Up @@ -2839,6 +2862,7 @@ public interface InterfaceWithGenericAnnotatedMethod<T> {
public static class ImplementsInterfaceWithGenericAnnotatedMethod
implements InterfaceWithGenericAnnotatedMethod<String> {

@Override
public void foo(String t) {
}
}
Expand All @@ -2852,6 +2876,7 @@ public static abstract class BaseClassWithGenericAnnotatedMethod<T> {
public static class ExtendsBaseClassWithGenericAnnotatedMethod
extends BaseClassWithGenericAnnotatedMethod<String> {

@Override
public void foo(String t) {
}
}
Expand Down

0 comments on commit de3c115

Please sign in to comment.