18
18
import java .lang .annotation .Annotation ;
19
19
import java .lang .annotation .ElementType ;
20
20
import java .lang .reflect .AnnotatedElement ;
21
- import java .lang .reflect .AnnotatedType ;
22
21
import java .lang .reflect .Executable ;
23
22
import java .lang .reflect .Method ;
24
23
import java .lang .reflect .Parameter ;
33
32
import java .util .function .Function ;
34
33
import java .util .function .Predicate ;
35
34
35
+ import org .springframework .core .annotation .AnnotatedElementUtils ;
36
+ import org .springframework .core .annotation .AnnotationUtils ;
36
37
import org .springframework .core .annotation .MergedAnnotations ;
37
38
import org .springframework .lang .NonNull ;
38
39
import org .springframework .lang .NonNullApi ;
39
40
import org .springframework .lang .NonNullFields ;
40
41
import org .springframework .lang .Nullable ;
41
42
import org .springframework .util .ClassUtils ;
42
43
import org .springframework .util .ConcurrentLruCache ;
44
+ import org .springframework .util .MultiValueMap ;
43
45
44
46
/**
45
47
* Default {@link Nullability.Introspector} implementation backed by {@link NullabilityProvider nullability providers}.
@@ -111,6 +113,11 @@ static DeclarationAnchor createTree(AnnotatedElement element) {
111
113
throw new IllegalArgumentException (String .format ("Cannot create DeclarationAnchor for %s" , element ));
112
114
}
113
115
116
+ @ Override
117
+ public boolean isDeclared (ElementType elementType ) {
118
+ return anchor .evaluate (elementType ) != Spec .UNSPECIFIED ;
119
+ }
120
+
114
121
@ Override
115
122
public Nullability .MethodNullability forMethod (Method method ) {
116
123
@@ -138,7 +145,7 @@ public Nullability forParameter(Parameter parameter) {
138
145
return new TheNullability (element .evaluate (ElementType .PARAMETER ));
139
146
}
140
147
141
- static < T > Spec doWith (Function <NullabilityProvider , Spec > function ) {
148
+ static Spec doWith (Function <NullabilityProvider , Spec > function ) {
142
149
143
150
for (NullabilityProvider provider : providers ) {
144
151
Spec result = function .apply (provider );
@@ -151,6 +158,34 @@ static <T> Spec doWith(Function<NullabilityProvider, Spec> function) {
151
158
return Spec .UNSPECIFIED ;
152
159
}
153
160
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
+
154
189
/**
155
190
* Provider for nullability rules.
156
191
*/
@@ -208,6 +243,7 @@ Spec evaluate(AnnotatedElement element, ElementType elementType) {
208
243
* {@code @Nonnull}/{@code @Nullable} directly or through meta-annotations that are composed of
209
244
* {@code @Nonnull}/{@code @Nullable} and {@code @TypeQualifierDefault}.
210
245
*/
246
+ @ SuppressWarnings ("DataFlowIssue" )
211
247
static class Jsr305Provider extends NullabilityProvider {
212
248
213
249
private static final Class <Annotation > NON_NULL = findClass ("javax.annotation.Nonnull" );
@@ -269,7 +305,7 @@ private static boolean test(Class<Annotation> annotationClass, Annotation annota
269
305
}
270
306
271
307
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" ,
273
309
(ElementType [] o ) -> Arrays .binarySearch (o , elementType ) >= 0 );
274
310
}
275
311
@@ -281,24 +317,27 @@ private static boolean isInScope(Annotation annotation, ElementType elementType)
281
317
* @return {@literal true} if the annotation expresses non-nullability.
282
318
*/
283
319
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 ()));
285
322
}
286
323
287
324
/**
288
325
* Introspect {@link Annotation} for being either a meta-annotation composed of {@code Nonnull} or {@code Nonnull}
289
326
* itself expressing nullability.
290
327
*
291
- * @param annotation
328
+ * @param annotation the annotation to introspect.
292
329
* @return {@literal true} if the annotation expresses nullability.
293
330
*/
294
331
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 ()));
296
334
}
297
335
}
298
336
299
337
/**
300
338
* Simplified variant of {@link Jsr305Provider} without {@code when} and {@code @TypeQualifierDefault} support.
301
339
*/
340
+ @ SuppressWarnings ("DataFlowIssue" )
302
341
static class JakartaAnnotationProvider extends SimpleAnnotationNullabilityProvider {
303
342
304
343
private static final Class <Annotation > NON_NULL = findClass ("jakarta.annotation.Nonnull" );
@@ -316,6 +355,7 @@ public static boolean isAvailable() {
316
355
/**
317
356
* Provider for JSpecify annotations.
318
357
*/
358
+ @ SuppressWarnings ("DataFlowIssue" )
319
359
static class JSpecifyAnnotationProvider extends NullabilityProvider {
320
360
321
361
private static final Class <Annotation > NON_NULL = findClass ("org.jspecify.annotations.NonNull" );
@@ -331,7 +371,6 @@ public static boolean isAvailable() {
331
371
Spec evaluate (AnnotatedElement element , ElementType elementType ) {
332
372
333
373
Annotation [] annotations = element .getAnnotations ();
334
- AnnotatedType annotatedType = null ;
335
374
336
375
if (element instanceof Parameter p ) {
337
376
@@ -478,8 +517,24 @@ public Nullability forParameter(Parameter parameter) {
478
517
}
479
518
}
480
519
520
+ /**
521
+ * Declaration result.
522
+ */
481
523
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
483
538
}
484
539
485
540
/**
@@ -490,8 +545,8 @@ interface DeclarationAnchor {
490
545
/**
491
546
* Evaluate nullability declarations for the given {@link ElementType}.
492
547
*
493
- * @param target
494
- * @return
548
+ * @param target target element type to evaluate declarations for.
549
+ * @return specification result.
495
550
*/
496
551
Spec evaluate (ElementType target );
497
552
0 commit comments