1818import java .lang .annotation .Annotation ;
1919import java .lang .annotation .ElementType ;
2020import java .lang .reflect .AnnotatedElement ;
21- import java .lang .reflect .AnnotatedType ;
2221import java .lang .reflect .Executable ;
2322import java .lang .reflect .Method ;
2423import java .lang .reflect .Parameter ;
3332import java .util .function .Function ;
3433import java .util .function .Predicate ;
3534
35+ import org .springframework .core .annotation .AnnotatedElementUtils ;
36+ import org .springframework .core .annotation .AnnotationUtils ;
3637import org .springframework .core .annotation .MergedAnnotations ;
3738import org .springframework .lang .NonNull ;
3839import org .springframework .lang .NonNullApi ;
3940import org .springframework .lang .NonNullFields ;
4041import org .springframework .lang .Nullable ;
4142import org .springframework .util .ClassUtils ;
4243import org .springframework .util .ConcurrentLruCache ;
44+ import org .springframework .util .MultiValueMap ;
4345
4446/**
4547 * Default {@link Nullability.Introspector} implementation backed by {@link NullabilityProvider nullability providers}.
@@ -111,6 +113,11 @@ static DeclarationAnchor createTree(AnnotatedElement element) {
111113 throw new IllegalArgumentException (String .format ("Cannot create DeclarationAnchor for %s" , element ));
112114 }
113115
116+ @ Override
117+ public boolean isDeclared (ElementType elementType ) {
118+ return anchor .evaluate (elementType ) != Spec .UNSPECIFIED ;
119+ }
120+
114121 @ Override
115122 public Nullability .MethodNullability forMethod (Method method ) {
116123
@@ -138,7 +145,7 @@ public Nullability forParameter(Parameter parameter) {
138145 return new TheNullability (element .evaluate (ElementType .PARAMETER ));
139146 }
140147
141- static < T > Spec doWith (Function <NullabilityProvider , Spec > function ) {
148+ static Spec doWith (Function <NullabilityProvider , Spec > function ) {
142149
143150 for (NullabilityProvider provider : providers ) {
144151 Spec result = function .apply (provider );
@@ -151,6 +158,34 @@ static <T> Spec doWith(Function<NullabilityProvider, Spec> function) {
151158 return Spec .UNSPECIFIED ;
152159 }
153160
161+ @ SuppressWarnings ("unchecked" )
162+ static <T > boolean test (Annotation annotation , String metaAnnotationName , String attribute , Predicate <T > filter ) {
163+
164+ if (annotation .annotationType ().getName ().equals (metaAnnotationName )) {
165+
166+ Map <String , Object > attributes = AnnotationUtils .getAnnotationAttributes (annotation );
167+
168+ return !attributes .isEmpty () && filter .test ((T ) attributes .get (attribute ));
169+ }
170+
171+ MultiValueMap <String , Object > attributes = AnnotatedElementUtils
172+ .getAllAnnotationAttributes (annotation .annotationType (), metaAnnotationName );
173+
174+ if (attributes == null || attributes .isEmpty ()) {
175+ return false ;
176+ }
177+
178+ List <Object > elementTypes = attributes .get (attribute );
179+
180+ for (Object value : elementTypes ) {
181+
182+ if (filter .test ((T ) value )) {
183+ return true ;
184+ }
185+ }
186+ return false ;
187+ }
188+
154189 /**
155190 * Provider for nullability rules.
156191 */
@@ -208,6 +243,7 @@ Spec evaluate(AnnotatedElement element, ElementType elementType) {
208243 * {@code @Nonnull}/{@code @Nullable} directly or through meta-annotations that are composed of
209244 * {@code @Nonnull}/{@code @Nullable} and {@code @TypeQualifierDefault}.
210245 */
246+ @ SuppressWarnings ("DataFlowIssue" )
211247 static class Jsr305Provider extends NullabilityProvider {
212248
213249 private static final Class <Annotation > NON_NULL = findClass ("javax.annotation.Nonnull" );
@@ -269,7 +305,7 @@ private static boolean test(Class<Annotation> annotationClass, Annotation annota
269305 }
270306
271307 private static boolean isInScope (Annotation annotation , ElementType elementType ) {
272- return NullableUtils .test (annotation , TYPE_QUALIFIER_CLASS_NAME , "value" ,
308+ return NullabilityIntrospector .test (annotation , TYPE_QUALIFIER_CLASS_NAME , "value" ,
273309 (ElementType [] o ) -> Arrays .binarySearch (o , elementType ) >= 0 );
274310 }
275311
@@ -281,24 +317,27 @@ private static boolean isInScope(Annotation annotation, ElementType elementType)
281317 * @return {@literal true} if the annotation expresses non-nullability.
282318 */
283319 static boolean isNonNull (Annotation annotation ) {
284- return NullableUtils .test (annotation , NON_NULL .getName (), "when" , o -> WHEN_NON_NULLABLE .contains (o .toString ()));
320+ return NullabilityIntrospector .test (annotation , NON_NULL .getName (), "when" ,
321+ o -> WHEN_NON_NULLABLE .contains (o .toString ()));
285322 }
286323
287324 /**
288325 * Introspect {@link Annotation} for being either a meta-annotation composed of {@code Nonnull} or {@code Nonnull}
289326 * itself expressing nullability.
290327 *
291- * @param annotation
328+ * @param annotation the annotation to introspect.
292329 * @return {@literal true} if the annotation expresses nullability.
293330 */
294331 static boolean isNullable (Annotation annotation ) {
295- return NullableUtils .test (annotation , NON_NULL .getName (), "when" , o -> WHEN_NULLABLE .contains (o .toString ()));
332+ return NullabilityIntrospector .test (annotation , NON_NULL .getName (), "when" ,
333+ o -> WHEN_NULLABLE .contains (o .toString ()));
296334 }
297335 }
298336
299337 /**
300338 * Simplified variant of {@link Jsr305Provider} without {@code when} and {@code @TypeQualifierDefault} support.
301339 */
340+ @ SuppressWarnings ("DataFlowIssue" )
302341 static class JakartaAnnotationProvider extends SimpleAnnotationNullabilityProvider {
303342
304343 private static final Class <Annotation > NON_NULL = findClass ("jakarta.annotation.Nonnull" );
@@ -316,6 +355,7 @@ public static boolean isAvailable() {
316355 /**
317356 * Provider for JSpecify annotations.
318357 */
358+ @ SuppressWarnings ("DataFlowIssue" )
319359 static class JSpecifyAnnotationProvider extends NullabilityProvider {
320360
321361 private static final Class <Annotation > NON_NULL = findClass ("org.jspecify.annotations.NonNull" );
@@ -331,7 +371,6 @@ public static boolean isAvailable() {
331371 Spec evaluate (AnnotatedElement element , ElementType elementType ) {
332372
333373 Annotation [] annotations = element .getAnnotations ();
334- AnnotatedType annotatedType = null ;
335374
336375 if (element instanceof Parameter p ) {
337376
@@ -478,8 +517,24 @@ public Nullability forParameter(Parameter parameter) {
478517 }
479518 }
480519
520+ /**
521+ * Declaration result.
522+ */
481523 enum Spec {
482- UNSPECIFIED , NULLABLE , NON_NULL
524+ /**
525+ * No nullabilty rule declared.
526+ */
527+ UNSPECIFIED ,
528+
529+ /**
530+ * Declaration yields nullable.
531+ */
532+ NULLABLE ,
533+
534+ /**
535+ * Declaration yields non-nullable.
536+ */
537+ NON_NULL
483538 }
484539
485540 /**
@@ -490,8 +545,8 @@ interface DeclarationAnchor {
490545 /**
491546 * Evaluate nullability declarations for the given {@link ElementType}.
492547 *
493- * @param target
494- * @return
548+ * @param target target element type to evaluate declarations for.
549+ * @return specification result.
495550 */
496551 Spec evaluate (ElementType target );
497552
0 commit comments