19
19
import java .lang .annotation .Annotation ;
20
20
import java .lang .reflect .AnnotatedElement ;
21
21
import java .lang .reflect .Method ;
22
+ import java .util .ArrayList ;
23
+ import java .util .Collection ;
24
+ import java .util .Collections ;
22
25
import java .util .HashMap ;
26
+ import java .util .HashSet ;
23
27
import java .util .List ;
24
28
import java .util .Map ;
29
+ import java .util .Set ;
30
+ import java .util .function .BiFunction ;
25
31
import java .util .function .Function ;
26
32
27
33
import org .springframework .core .annotation .AnnotationConfigurationException ;
55
61
*/
56
62
final class AuthorizationAnnotationUtils {
57
63
58
- static <A extends Annotation > Function < AnnotatedElement , A > withDefaults (Class <A > type ,
64
+ static <A extends Annotation > BiFunction < Method , Class <?> , A > withDefaults (Class <A > type ,
59
65
PrePostTemplateDefaults defaults ) {
60
66
Function <MergedAnnotation <A >, A > map = (mergedAnnotation ) -> {
61
67
if (mergedAnnotation .getMetaSource () == null ) {
@@ -79,19 +85,25 @@ static <A extends Annotation> Function<AnnotatedElement, A> withDefaults(Class<A
79
85
properties .put ("value" , value );
80
86
return MergedAnnotation .of (annotatedElement , type , properties ).synthesize ();
81
87
};
82
- return (annotatedElement ) -> findDistinctAnnotation (annotatedElement , type , map );
88
+ return (method , targetClass ) -> findDistinctAnnotation (method , targetClass , type , map );
83
89
}
84
90
85
- static <A extends Annotation > Function <AnnotatedElement , A > withDefaults (Class <A > type ) {
86
- return (annotatedElement ) -> findDistinctAnnotation (annotatedElement , type , MergedAnnotation ::synthesize );
91
+ static <A extends Annotation > BiFunction <Method , Class <?>, A > withDefaults (Class <A > type ) {
92
+ return (method , targetClass ) -> findDistinctAnnotation (method , targetClass , type , MergedAnnotation ::synthesize );
93
+ }
94
+
95
+ static BiFunction <Method , Class <?>, Annotation > withDefaults (
96
+ Collection <Class <? extends Annotation >> annotationTypes ) {
97
+ return (method , targetClass ) -> findDistinctAnnotation (method , targetClass , annotationTypes ,
98
+ MergedAnnotation ::synthesize );
87
99
}
88
100
89
101
static <A extends Annotation > A findUniqueAnnotation (Method method , Class <A > annotationType ) {
90
- return findDistinctAnnotation (method , annotationType , MergedAnnotation ::synthesize );
102
+ return findDistinctAnnotation (method , method . getDeclaringClass (), annotationType , MergedAnnotation ::synthesize );
91
103
}
92
104
93
105
static <A extends Annotation > A findUniqueAnnotation (Class <?> type , Class <A > annotationType ) {
94
- return findDistinctAnnotation (type , annotationType , MergedAnnotation ::synthesize );
106
+ return findDistinctAnnotation (null , type , annotationType , MergedAnnotation ::synthesize );
95
107
}
96
108
97
109
/**
@@ -110,7 +122,7 @@ static <A extends Annotation> A findUniqueAnnotation(Class<?> type, Class<A> ann
110
122
*/
111
123
static <A extends Annotation > A findUniqueAnnotation (Method method , Class <A > annotationType ,
112
124
Function <MergedAnnotation <A >, A > map ) {
113
- return findDistinctAnnotation (method , annotationType , map );
125
+ return findDistinctAnnotation (method , method . getDeclaringClass (), annotationType , map );
114
126
}
115
127
116
128
/**
@@ -129,41 +141,114 @@ static <A extends Annotation> A findUniqueAnnotation(Method method, Class<A> ann
129
141
*/
130
142
static <A extends Annotation > A findUniqueAnnotation (Class <?> type , Class <A > annotationType ,
131
143
Function <MergedAnnotation <A >, A > map ) {
132
- return findDistinctAnnotation (type , annotationType , map );
144
+ return findDistinctAnnotation (null , type , annotationType , map );
133
145
}
134
146
135
- private static <A extends Annotation > A findDistinctAnnotation (AnnotatedElement annotatedElement ,
147
+ private static <A extends Annotation > A findDistinctAnnotation (Method method , Class <?> targetClass ,
136
148
Class <A > annotationType , Function <MergedAnnotation <A >, A > map ) {
137
- MergedAnnotations mergedAnnotations = MergedAnnotations .from (annotatedElement , SearchStrategy .DIRECT ,
138
- RepeatableContainers .none ());
139
- List <A > annotations = mergedAnnotations .stream (annotationType )
140
- .map (MergedAnnotation ::withNonMergedAttributes )
141
- .map (map )
142
- .distinct ()
143
- .toList ();
149
+ Function <MergedAnnotation <Annotation >, Annotation > generic = (merged ) -> map
150
+ .apply ((MergedAnnotation <A >) merged );
151
+ return (A ) findDistinctAnnotation (method , targetClass , List .of (annotationType ), generic );
152
+ }
144
153
145
- if (annotations .isEmpty ()) {
146
- mergedAnnotations = MergedAnnotations .from (annotatedElement , SearchStrategy .TYPE_HIERARCHY ,
147
- RepeatableContainers .none ());
148
- annotations = mergedAnnotations .stream (annotationType )
149
- .map (MergedAnnotation ::withNonMergedAttributes )
150
- .map (map )
151
- .distinct ()
152
- .toList ();
154
+ private static <A extends Annotation > A findDistinctAnnotation (Method method , Class <?> targetClass ,
155
+ Collection <Class <? extends Annotation >> annotationTypes ,
156
+ Function <MergedAnnotation <Annotation >, Annotation > map ) {
157
+ List <AnnotationScore <Annotation >> scores = findAnnotations (method , targetClass , annotationTypes , map ,
158
+ new HashSet <>(), 1 );
159
+ int lowestScore = Integer .MAX_VALUE ;
160
+ List <Annotation > annotations = new ArrayList <>();
161
+ for (AnnotationScore <Annotation > score : scores ) {
162
+ if (score .score < lowestScore ) {
163
+ annotations = new ArrayList <>();
164
+ annotations .add (score .annotation );
165
+ lowestScore = score .score ;
166
+ }
167
+ else if (score .score == lowestScore ) {
168
+ annotations .add (score .annotation );
169
+ }
153
170
}
154
-
155
171
return switch (annotations .size ()) {
156
172
case 0 -> null ;
157
- case 1 -> annotations .get (0 );
173
+ case 1 -> ( A ) annotations .get (0 );
158
174
default -> throw new AnnotationConfigurationException ("""
159
175
Please ensure there is one unique annotation of type @%s attributed to %s. \
160
- Found %d competing annotations: %s""" .formatted (annotationType . getName (), annotatedElement ,
161
- annotations . size (), annotations ));
176
+ Found %d competing annotations: %s""" .formatted (annotationTypes , method , annotations . size () ,
177
+ annotations ));
162
178
};
163
179
}
164
180
181
+ private static List <AnnotationScore <Annotation >> findAnnotations (Method method , Class <?> declaringClass ,
182
+ Collection <Class <? extends Annotation >> annotationTypes ,
183
+ Function <MergedAnnotation <Annotation >, Annotation > map , Set <Class <?>> visited , int distance ) {
184
+ if (declaringClass == null || visited .contains (declaringClass ) || declaringClass == Object .class ) {
185
+ return Collections .emptyList ();
186
+ }
187
+ visited .add (declaringClass );
188
+
189
+ Method methodToUse = methodMatching (method , declaringClass );
190
+ if (methodToUse != null ) {
191
+ List <AnnotationScore <Annotation >> scores = findAnnotations (methodToUse , annotationTypes , map ,
192
+ distance * 11 );
193
+ if (!scores .isEmpty ()) {
194
+ return scores ;
195
+ }
196
+ }
197
+
198
+ List <AnnotationScore <Annotation >> scores = findAnnotations (declaringClass , annotationTypes , map , distance * 13 );
199
+ if (!scores .isEmpty ()) {
200
+ return scores ;
201
+ }
202
+
203
+ scores .addAll (findAnnotations (methodToUse , declaringClass .getSuperclass (), annotationTypes , map , visited ,
204
+ distance + 1 ));
205
+ for (Class <?> inter : declaringClass .getInterfaces ()) {
206
+ scores .addAll (findAnnotations (methodToUse , inter , annotationTypes , map , visited , distance + 1 ));
207
+ }
208
+ return scores ;
209
+ }
210
+
211
+ private static Method methodMatching (Method method , Class <?> candidate ) {
212
+ if (method == null ) {
213
+ return null ;
214
+ }
215
+ if (method .getDeclaringClass () == candidate ) {
216
+ return method ;
217
+ }
218
+ try {
219
+ return candidate .getDeclaredMethod (method .getName (), method .getParameterTypes ());
220
+ }
221
+ catch (Exception ex ) {
222
+ return method ;
223
+ }
224
+ }
225
+
226
+ private static List <AnnotationScore <Annotation >> findAnnotations (AnnotatedElement element ,
227
+ Collection <Class <? extends Annotation >> annotationTypes ,
228
+ Function <MergedAnnotation <Annotation >, Annotation > map , int score ) {
229
+ List <Annotation > annotations = MergedAnnotations
230
+ .from (element , SearchStrategy .DIRECT , RepeatableContainers .none ())
231
+ .stream ()
232
+ .filter ((annotation ) -> annotationTypes .contains (annotation .getType ()))
233
+ .map (MergedAnnotation ::withNonMergedAttributes )
234
+ .map (map )
235
+ .toList ();
236
+ List <AnnotationScore <Annotation >> scores = new ArrayList <>();
237
+ for (Annotation annotation : annotations ) {
238
+ scores .add (new AnnotationScore <>(annotation , score ));
239
+ }
240
+ return scores ;
241
+ }
242
+
165
243
private AuthorizationAnnotationUtils () {
166
244
167
245
}
168
246
247
+ private record AnnotationScore <A extends Annotation >(A annotation , int score ) {
248
+ @ Override
249
+ public String toString () {
250
+ return this .annotation .toString ();
251
+ }
252
+ }
253
+
169
254
}
0 commit comments