48
48
* <p>Support for meta-annotations with <em>attribute overrides</em> in
49
49
* <em>composed annotations</em> is provided by all variants of the
50
50
* {@code getMergedAnnotationAttributes()}, {@code getMergedAnnotation()},
51
+ * {@code getAllMergedAnnotations()}, {@code getMergedRepeatableAnnotations()},
51
52
* {@code findMergedAnnotationAttributes()}, {@code findMergedAnnotation()},
52
53
* {@code findAllMergedAnnotations()}, and {@code findMergedRepeatableAnnotations()}
53
54
* methods.
@@ -150,7 +151,8 @@ public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Class
150
151
try {
151
152
Annotation annotation = element .getAnnotation (annotationType );
152
153
if (annotation != null ) {
153
- searchWithGetSemantics (annotation .annotationType (), annotationType , null , new SimpleAnnotationProcessor <Object >() {
154
+ searchWithGetSemantics (annotation .annotationType (), annotationType , null , null ,
155
+ new SimpleAnnotationProcessor <Object >() {
154
156
@ Override
155
157
public Object process (AnnotatedElement annotatedElement , Annotation annotation , int metaDepth ) {
156
158
types .add (annotation .annotationType ().getName ());
@@ -189,7 +191,8 @@ public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Strin
189
191
try {
190
192
Annotation annotation = AnnotationUtils .getAnnotation (element , annotationName );
191
193
if (annotation != null ) {
192
- searchWithGetSemantics (annotation .annotationType (), null , annotationName , new SimpleAnnotationProcessor <Object >() {
194
+ searchWithGetSemantics (annotation .annotationType (), null , annotationName , null ,
195
+ new SimpleAnnotationProcessor <Object >() {
193
196
@ Override
194
197
public Object process (AnnotatedElement annotatedElement , Annotation annotation , int metaDepth ) {
195
198
types .add (annotation .annotationType ().getName ());
@@ -409,6 +412,7 @@ public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElemen
409
412
public static AnnotationAttributes getMergedAnnotationAttributes (AnnotatedElement element ,
410
413
String annotationName , boolean classValuesAsString , boolean nestedAnnotationsAsMap ) {
411
414
415
+ Assert .hasLength (annotationName , "annotationName must not be null or empty" );
412
416
AnnotationAttributes attributes = searchWithGetSemantics (element , null , annotationName ,
413
417
new MergedAnnotationAttributesProcessor (null , annotationName , classValuesAsString , nestedAnnotationsAsMap ));
414
418
AnnotationUtils .postProcessAnnotationAttributes (element , attributes , classValuesAsString , nestedAnnotationsAsMap );
@@ -483,6 +487,81 @@ public static <A extends Annotation> Set<A> getAllMergedAnnotations(AnnotatedEle
483
487
return postProcessAndSynthesizeAggregatedResults (element , annotationType , processor .getAggregatedResults ());
484
488
}
485
489
490
+ /**
491
+ * Get all <em>repeatable annotations</em> of the specified {@code annotationType}
492
+ * within the annotation hierarchy <em>above</em> the supplied {@code element};
493
+ * and for each annotation found, merge that annotation's attributes with
494
+ * <em>matching</em> attributes from annotations in lower levels of the annotation
495
+ * hierarchy and synthesize the results back into an annotation of the specified
496
+ * {@code annotationType}.
497
+ * <p>The container type that holds the repeatable annotations will be looked up
498
+ * via {@link java.lang.annotation.Repeatable}.
499
+ * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
500
+ * single annotation and within annotation hierarchies.
501
+ * <p>This method follows <em>get semantics</em> as described in the
502
+ * {@linkplain AnnotatedElementUtils class-level javadoc}.
503
+ * @param element the annotated element; never {@code null}
504
+ * @param annotationType the annotation type to find; never {@code null}
505
+ * @return the set of all merged repeatable {@code Annotations} found, or an empty
506
+ * set if none were found
507
+ * @since 4.3
508
+ * @see #getMergedAnnotation(AnnotatedElement, Class)
509
+ * @see #getAllMergedAnnotations(AnnotatedElement, Class)
510
+ * @see #getMergedRepeatableAnnotations(AnnotatedElement, Class, Class)
511
+ * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
512
+ * is {@code null}, or if the container type cannot be resolved
513
+ */
514
+ public static <A extends Annotation > Set <A > getMergedRepeatableAnnotations (AnnotatedElement element ,
515
+ Class <A > annotationType ) {
516
+
517
+ return getMergedRepeatableAnnotations (element , annotationType , null );
518
+ }
519
+
520
+ /**
521
+ * Get all <em>repeatable annotations</em> of the specified {@code annotationType}
522
+ * within the annotation hierarchy <em>above</em> the supplied {@code element};
523
+ * and for each annotation found, merge that annotation's attributes with
524
+ * <em>matching</em> attributes from annotations in lower levels of the annotation
525
+ * hierarchy and synthesize the results back into an annotation of the specified
526
+ * {@code annotationType}.
527
+ * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
528
+ * single annotation and within annotation hierarchies.
529
+ * <p>This method follows <em>get semantics</em> as described in the
530
+ * {@linkplain AnnotatedElementUtils class-level javadoc}.
531
+ * @param element the annotated element; never {@code null}
532
+ * @param annotationType the annotation type to find; never {@code null}
533
+ * @param containerType the type of the container that holds the annotations;
534
+ * may be {@code null} if the container type should be looked up via
535
+ * {@link java.lang.annotation.Repeatable}
536
+ * @return the set of all merged repeatable {@code Annotations} found, or an empty
537
+ * set if none were found
538
+ * @since 4.3
539
+ * @see #getMergedAnnotation(AnnotatedElement, Class)
540
+ * @see #getAllMergedAnnotations(AnnotatedElement, Class)
541
+ * @throws IllegalArgumentException if the {@code element} or {@code annotationType}
542
+ * is {@code null}, or if the container type cannot be resolved
543
+ * @throws AnnotationConfigurationException if the supplied {@code containerType}
544
+ * is not a valid container annotation for the supplied {@code annotationType}
545
+ */
546
+ public static <A extends Annotation > Set <A > getMergedRepeatableAnnotations (AnnotatedElement element ,
547
+ Class <A > annotationType , Class <? extends Annotation > containerType ) {
548
+
549
+ Assert .notNull (element , "AnnotatedElement must not be null" );
550
+ Assert .notNull (annotationType , "annotationType must not be null" );
551
+
552
+ if (containerType == null ) {
553
+ containerType = resolveContainerType (annotationType );
554
+ }
555
+ else {
556
+ validateRepeatableContainerType (annotationType , containerType );
557
+ }
558
+
559
+ MergedAnnotationAttributesProcessor processor =
560
+ new MergedAnnotationAttributesProcessor (annotationType , null , false , false , true );
561
+ searchWithGetSemantics (element , annotationType , null , containerType , processor );
562
+ return postProcessAndSynthesizeAggregatedResults (element , annotationType , processor .getAggregatedResults ());
563
+ }
564
+
486
565
/**
487
566
* Get the annotation attributes of <strong>all</strong> annotations of the specified
488
567
* {@code annotationName} in the annotation hierarchy above the supplied
@@ -832,12 +911,32 @@ public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(Anno
832
911
* @param processor the processor to delegate to
833
912
* @return the result of the processor, potentially {@code null}
834
913
*/
835
- private static <T > T searchWithGetSemantics (AnnotatedElement element ,
836
- Class <? extends Annotation > annotationType , String annotationName , Processor <T > processor ) {
914
+ private static <T > T searchWithGetSemantics (AnnotatedElement element , Class <? extends Annotation > annotationType ,
915
+ String annotationName , Processor <T > processor ) {
916
+
917
+ return searchWithGetSemantics (element , annotationType , annotationName , null , processor );
918
+ }
919
+
920
+ /**
921
+ * Search for annotations of the specified {@code annotationName} or
922
+ * {@code annotationType} on the specified {@code element}, following
923
+ * <em>get semantics</em>.
924
+ * @param element the annotated element
925
+ * @param annotationType the annotation type to find
926
+ * @param annotationName the fully qualified class name of the annotation
927
+ * type to find (as an alternative to {@code annotationType})
928
+ * @param containerType the type of the container that holds repeatable
929
+ * annotations, or {@code null} if the annotation is not repeatable
930
+ * @param processor the processor to delegate to
931
+ * @return the result of the processor, potentially {@code null}
932
+ * @since 4.3
933
+ */
934
+ private static <T > T searchWithGetSemantics (AnnotatedElement element , Class <? extends Annotation > annotationType ,
935
+ String annotationName , Class <? extends Annotation > containerType , Processor <T > processor ) {
837
936
838
937
try {
839
- return searchWithGetSemantics (
840
- element , annotationType , annotationName , processor , new HashSet <AnnotatedElement >(), 0 );
938
+ return searchWithGetSemantics (element , annotationType , annotationName , containerType , processor ,
939
+ new HashSet <AnnotatedElement >(), 0 );
841
940
}
842
941
catch (Throwable ex ) {
843
942
AnnotationUtils .rethrowAnnotationConfigurationException (ex );
@@ -855,14 +954,16 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element,
855
954
* @param annotationType the annotation type to find
856
955
* @param annotationName the fully qualified class name of the annotation
857
956
* type to find (as an alternative to {@code annotationType})
957
+ * @param containerType the type of the container that holds repeatable
958
+ * annotations, or {@code null} if the annotation is not repeatable
858
959
* @param processor the processor to delegate to
859
960
* @param visited the set of annotated elements that have already been visited
860
961
* @param metaDepth the meta-depth of the annotation
861
962
* @return the result of the processor, potentially {@code null}
862
963
*/
863
- private static <T > T searchWithGetSemantics (AnnotatedElement element ,
864
- Class <? extends Annotation > annotationType , String annotationName ,
865
- Processor < T > processor , Set <AnnotatedElement > visited , int metaDepth ) {
964
+ private static <T > T searchWithGetSemantics (AnnotatedElement element , Class <? extends Annotation > annotationType ,
965
+ String annotationName , Class <? extends Annotation > containerType , Processor < T > processor ,
966
+ Set <AnnotatedElement > visited , int metaDepth ) {
866
967
867
968
Assert .notNull (element , "AnnotatedElement must not be null" );
868
969
@@ -871,12 +972,12 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element,
871
972
// Start searching within locally declared annotations
872
973
List <Annotation > declaredAnnotations = Arrays .asList (element .getDeclaredAnnotations ());
873
974
T result = searchWithGetSemanticsInAnnotations (element , declaredAnnotations ,
874
- annotationType , annotationName , processor , visited , metaDepth );
975
+ annotationType , annotationName , containerType , processor , visited , metaDepth );
875
976
if (result != null ) {
876
977
return result ;
877
978
}
878
979
879
- if (element instanceof Class ) { // otherwise getAnnotations doesn't return anything new
980
+ if (element instanceof Class ) { // otherwise getAnnotations doesn't return anything new
880
981
List <Annotation > inheritedAnnotations = new ArrayList <Annotation >();
881
982
for (Annotation annotation : element .getAnnotations ()) {
882
983
if (!declaredAnnotations .contains (annotation )) {
@@ -886,7 +987,7 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element,
886
987
887
988
// Continue searching within inherited annotations
888
989
result = searchWithGetSemanticsInAnnotations (element , inheritedAnnotations ,
889
- annotationType , annotationName , processor , visited , metaDepth );
990
+ annotationType , annotationName , containerType , processor , visited , metaDepth );
890
991
if (result != null ) {
891
992
return result ;
892
993
}
@@ -915,6 +1016,8 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element,
915
1016
* @param annotationType the annotation type to find
916
1017
* @param annotationName the fully qualified class name of the annotation
917
1018
* type to find (as an alternative to {@code annotationType})
1019
+ * @param containerType the type of the container that holds repeatable
1020
+ * annotations, or {@code null} if the annotation is not repeatable
918
1021
* @param processor the processor to delegate to
919
1022
* @param visited the set of annotated elements that have already been visited
920
1023
* @param metaDepth the meta-depth of the annotation
@@ -923,21 +1026,39 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element,
923
1026
*/
924
1027
private static <T > T searchWithGetSemanticsInAnnotations (AnnotatedElement annotatedElement ,
925
1028
List <Annotation > annotations , Class <? extends Annotation > annotationType , String annotationName ,
926
- Processor <T > processor , Set <AnnotatedElement > visited , int metaDepth ) {
1029
+ Class <? extends Annotation > containerType , Processor <T > processor , Set <AnnotatedElement > visited ,
1030
+ int metaDepth ) {
927
1031
928
1032
// Search in annotations
929
1033
for (Annotation annotation : annotations ) {
930
- // Note: we only check for (metaDepth > 0) due to the nuances of getMetaAnnotationTypes().
931
- if (!AnnotationUtils .isInJavaLangAnnotationPackage (annotation ) &&
932
- ((annotation .annotationType () == annotationType
933
- || annotation .annotationType ().getName ().equals (annotationName )) || metaDepth > 0 )) {
934
- T result = processor .process (annotatedElement , annotation , metaDepth );
935
- if (result != null ) {
936
- if (processor .aggregates () && metaDepth == 0 ) {
937
- processor .getAggregatedResults ().add (result );
1034
+ if (!AnnotationUtils .isInJavaLangAnnotationPackage (annotation )) {
1035
+
1036
+ // TODO Check non-repeatable annotations first, once we have sorted out
1037
+ // the metaDepth nuances of getMetaAnnotationTypes().
1038
+
1039
+ // Repeatable annotations in container?
1040
+ if (annotation .annotationType () == containerType ) {
1041
+ for (Annotation contained : getRawAnnotationsFromContainer (annotatedElement , annotation )) {
1042
+ T result = processor .process (annotatedElement , contained , metaDepth );
1043
+ if (result != null ) {
1044
+ // No need to post-process since repeatable annotations within a
1045
+ // container cannot be composed annotations.
1046
+ processor .getAggregatedResults ().add (result );
1047
+ }
938
1048
}
939
- else {
940
- return result ;
1049
+ }
1050
+ else if ((annotation .annotationType () == annotationType
1051
+ || annotation .annotationType ().getName ().equals (annotationName )) || metaDepth > 0 ) {
1052
+
1053
+ // Note: we only check for (metaDepth > 0) due to the nuances of getMetaAnnotationTypes().
1054
+ T result = processor .process (annotatedElement , annotation , metaDepth );
1055
+ if (result != null ) {
1056
+ if (processor .aggregates () && metaDepth == 0 ) {
1057
+ processor .getAggregatedResults ().add (result );
1058
+ }
1059
+ else {
1060
+ return result ;
1061
+ }
941
1062
}
942
1063
}
943
1064
}
@@ -947,7 +1068,7 @@ private static <T> T searchWithGetSemanticsInAnnotations(AnnotatedElement annota
947
1068
for (Annotation annotation : annotations ) {
948
1069
if (!AnnotationUtils .isInJavaLangAnnotationPackage (annotation )) {
949
1070
T result = searchWithGetSemantics (annotation .annotationType (), annotationType ,
950
- annotationName , processor , visited , metaDepth + 1 );
1071
+ annotationName , containerType , processor , visited , metaDepth + 1 );
951
1072
if (result != null ) {
952
1073
processor .postProcess (annotatedElement , annotation , result );
953
1074
if (processor .aggregates () && metaDepth == 0 ) {
0 commit comments