@@ -424,6 +424,7 @@ public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElemen
424424 * @param annotationType the annotation type to find
425425 * @return the merged, synthesized {@code Annotation}, or {@code null} if not found
426426 * @since 4.2
427+ * @see #findAllMergedAnnotations(AnnotatedElement, Class)
427428 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)
428429 * @see #getMergedAnnotationAttributes(AnnotatedElement, Class)
429430 */
@@ -460,6 +461,41 @@ public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement ele
460461 return AnnotationUtils .synthesizeAnnotation (attributes , (Class <A >) attributes .annotationType (), element );
461462 }
462463
464+ /**
465+ * Find <strong>all</strong> annotations of the specified {@code annotationType}
466+ * within the annotation hierarchy <em>above</em> the supplied {@code element};
467+ * and for each annotation found, merge that annotation's attributes with
468+ * <em>matching</em> attributes from annotations in lower levels of the annotation
469+ * hierarchy, and synthesize the result back into an annotation of the specified
470+ * {@code annotationType}.
471+ * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
472+ * single annotation and within the annotation hierarchy.
473+ * @param element the annotated element; never {@code null}
474+ * @param annotationType the annotation type to find; never {@code null}
475+ * @return the set of all merged, synthesized {@code Annotations} found, or an empty
476+ * set if none were found
477+ * @since 4.3
478+ * @see #findMergedAnnotation(AnnotatedElement, Class)
479+ */
480+ public static <A extends Annotation > Set <A > findAllMergedAnnotations (AnnotatedElement element ,
481+ Class <A > annotationType ) {
482+
483+ Assert .notNull (element , "AnnotatedElement must not be null" );
484+ Assert .notNull (annotationType , "annotationType must not be null" );
485+
486+ MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor (annotationType , null ,
487+ false , false , true );
488+
489+ searchWithFindSemantics (element , annotationType , annotationType .getName (), processor );
490+
491+ Set <A > annotations = new LinkedHashSet <A >();
492+ for (AnnotationAttributes attributes : processor .getAggregatedResults ()) {
493+ AnnotationUtils .postProcessAnnotationAttributes (element , attributes , false , false );
494+ annotations .add (AnnotationUtils .synthesizeAnnotation (attributes , annotationType , element ));
495+ }
496+ return annotations ;
497+ }
498+
463499 /**
464500 * Find the first annotation of the specified {@code annotationType} within
465501 * the annotation hierarchy <em>above</em> the supplied {@code element} and
@@ -796,6 +832,8 @@ private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? e
796832 // Locally declared annotations (ignoring @Inherited)
797833 Annotation [] annotations = element .getDeclaredAnnotations ();
798834
835+ List <T > aggregatedResults = processor .aggregates () ? new ArrayList <T >() : null ;
836+
799837 // Search in local annotations
800838 for (Annotation annotation : annotations ) {
801839 if (!AnnotationUtils .isInJavaLangAnnotationPackage (annotation ) &&
@@ -804,7 +842,12 @@ private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? e
804842 metaDepth > 0 )) {
805843 T result = processor .process (element , annotation , metaDepth );
806844 if (result != null ) {
807- return result ;
845+ if (processor .aggregates () && metaDepth == 0 ) {
846+ aggregatedResults .add (result );
847+ }
848+ else {
849+ return result ;
850+ }
808851 }
809852 }
810853 }
@@ -816,11 +859,20 @@ private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? e
816859 annotation .annotationType (), annotationType , annotationName , processor , visited , metaDepth + 1 );
817860 if (result != null ) {
818861 processor .postProcess (annotation .annotationType (), annotation , result );
819- return result ;
862+ if (processor .aggregates () && metaDepth == 0 ) {
863+ aggregatedResults .add (result );
864+ }
865+ else {
866+ return result ;
867+ }
820868 }
821869 }
822870 }
823871
872+ if (processor .aggregates ()) {
873+ processor .getAggregatedResults ().addAll (0 , aggregatedResults );
874+ }
875+
824876 if (element instanceof Method ) {
825877 Method method = (Method ) element ;
826878
@@ -930,11 +982,16 @@ private static <T> T searchOnInterfaces(Method method, Class<? extends Annotatio
930982 * annotations, or all annotations discovered by the currently executing
931983 * search. The term "target" in this context refers to a matching
932984 * annotation (i.e., a specific annotation type that was found during
933- * the search). Returning a non-null value from the {@link #process}
985+ * the search).
986+ * <p>Returning a non-null value from the {@link #process}
934987 * method instructs the search algorithm to stop searching further;
935988 * whereas, returning {@code null} from the {@link #process} method
936989 * instructs the search algorithm to continue searching for additional
937- * annotations.
990+ * annotations. One exception to this rule applies to processors
991+ * that {@linkplain #aggregates aggregate} results. If an aggregating
992+ * processor returns a non-null value, that value will be added to the
993+ * list of {@linkplain #getAggregatedResults aggregated results}
994+ * and the search algorithm will continue.
938995 * <p>Processors can optionally {@linkplain #postProcess post-process}
939996 * the result of the {@link #process} method as the search algorithm
940997 * goes back down the annotation hierarchy from an invocation of
@@ -983,12 +1040,38 @@ private interface Processor<T> {
9831040 * @param result the result to post-process
9841041 */
9851042 void postProcess (AnnotatedElement annotatedElement , Annotation annotation , T result );
986- }
9871043
1044+ /**
1045+ * Determine if this processor aggregates the results returned by {@link #process}.
1046+ * <p>If this method returns {@code true}, then {@link #getAggregatedResults()}
1047+ * must return a non-null value.
1048+ * <p>WARNING: aggregation is currently only supported for <em>find semantics</em>.
1049+ * @return {@code true} if this processor supports aggregated results
1050+ * @see #getAggregatedResults
1051+ * @since 4.3
1052+ */
1053+ boolean aggregates ();
1054+
1055+ /**
1056+ * Get the list of results aggregated by this processor.
1057+ * <p>NOTE: the processor does not aggregate the results itself.
1058+ * Rather, the search algorithm that uses this processor is responsible
1059+ * for asking this processor if it {@link #aggregates} results and then
1060+ * adding the post-processed results to the list returned by this
1061+ * method.
1062+ * <p>WARNING: aggregation is currently only supported for <em>find semantics</em>.
1063+ * @return the list of results aggregated by this processor; never
1064+ * {@code null} unless {@link #aggregates} returns {@code false}
1065+ * @see #aggregates
1066+ * @since 4.3
1067+ */
1068+ List <T > getAggregatedResults ();
1069+ }
9881070
9891071 /**
990- * {@link Processor} that {@linkplain #process processes} annotations
991- * but does not {@linkplain #postProcess post-process} results.
1072+ * {@link Processor} that {@linkplain #process(AnnotatedElement, Annotation, int)
1073+ * processes} annotations but does not {@linkplain #postProcess post-process} or
1074+ * {@linkplain #aggregates aggregate} results.
9921075 * @since 4.2
9931076 */
9941077 private abstract static class SimpleAnnotationProcessor <T > implements Processor <T > {
@@ -997,6 +1080,16 @@ private abstract static class SimpleAnnotationProcessor<T> implements Processor<
9971080 public final void postProcess (AnnotatedElement annotatedElement , Annotation annotation , T result ) {
9981081 // no-op
9991082 }
1083+
1084+ @ Override
1085+ public final boolean aggregates () {
1086+ return false ;
1087+ }
1088+
1089+ @ Override
1090+ public List <T > getAggregatedResults () {
1091+ throw new UnsupportedOperationException ("SimpleAnnotationProcessor does not support aggregated results" );
1092+ }
10001093 }
10011094
10021095
@@ -1019,13 +1112,33 @@ private static class MergedAnnotationAttributesProcessor implements Processor<An
10191112
10201113 private final boolean nestedAnnotationsAsMap ;
10211114
1115+ private final List <AnnotationAttributes > aggregatedResults ;
1116+
1117+
10221118 MergedAnnotationAttributesProcessor (Class <? extends Annotation > annotationType , String annotationName ,
10231119 boolean classValuesAsString , boolean nestedAnnotationsAsMap ) {
10241120
1121+ this (annotationType , annotationName , classValuesAsString , nestedAnnotationsAsMap , false );
1122+ }
1123+
1124+ MergedAnnotationAttributesProcessor (Class <? extends Annotation > annotationType , String annotationName ,
1125+ boolean classValuesAsString , boolean nestedAnnotationsAsMap , boolean aggregates ) {
1126+
10251127 this .annotationType = annotationType ;
10261128 this .annotationName = annotationName ;
10271129 this .classValuesAsString = classValuesAsString ;
10281130 this .nestedAnnotationsAsMap = nestedAnnotationsAsMap ;
1131+ this .aggregatedResults = (aggregates ? new ArrayList <AnnotationAttributes >() : null );
1132+ }
1133+
1134+ @ Override
1135+ public boolean aggregates () {
1136+ return this .aggregatedResults != null ;
1137+ }
1138+
1139+ @ Override
1140+ public List <AnnotationAttributes > getAggregatedResults () {
1141+ return this .aggregatedResults ;
10291142 }
10301143
10311144 @ Override
0 commit comments