001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.annotation;
018
019import java.lang.annotation.Annotation;
020import java.lang.annotation.Inherited;
021import java.lang.reflect.AnnotatedElement;
022import java.lang.reflect.Proxy;
023import java.util.EnumSet;
024import java.util.List;
025import java.util.Map;
026import java.util.NoSuchElementException;
027import java.util.Optional;
028import java.util.Set;
029import java.util.function.Function;
030import java.util.function.Predicate;
031
032import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
033import org.springframework.lang.Nullable;
034
035/**
036 * A single merged annotation returned from a {@link MergedAnnotations}
037 * collection. Presents a view onto an annotation where attribute values may
038 * have been "merged" from different source values.
039 *
040 * <p>Attribute values may be accessed using the various {@code get} methods.
041 * For example, to access an {@code int} attribute the {@link #getInt(String)}
042 * method would be used.
043 *
044 * <p>Note that attribute values are <b>not</b> converted when accessed.
045 * For example, it is not possible to call {@link #getString(String)} if the
046 * underlying attribute is an {@code int}. The only exception to this rule is
047 * {@code Class} and {@code Class[]} values which may be accessed as
048 * {@code String} and {@code String[]} respectively to prevent potential early
049 * class initialization.
050 *
051 * <p>If necessary, a {@code MergedAnnotation} can be {@linkplain #synthesize()
052 * synthesized} back into an actual {@link java.lang.annotation.Annotation}.
053 *
054 * @author Phillip Webb
055 * @author Juergen Hoeller
056 * @author Sam Brannen
057 * @since 5.2
058 * @param <A> the annotation type
059 * @see MergedAnnotations
060 * @see MergedAnnotationPredicates
061 */
062public interface MergedAnnotation<A extends Annotation> {
063
064        /**
065         * The attribute name for annotations with a single element.
066         */
067        String VALUE = "value";
068
069
070        /**
071         * Get the {@code Class} reference for the actual annotation type.
072         * @return the annotation type
073         */
074        Class<A> getType();
075
076        /**
077         * Determine if the annotation is present on the source. Considers
078         * {@linkplain #isDirectlyPresent() directly present} and
079         * {@linkplain #isMetaPresent() meta-present} annotations within the context
080         * of the {@link SearchStrategy} used.
081         * @return {@code true} if the annotation is present
082         */
083        boolean isPresent();
084
085        /**
086         * Determine if the annotation is directly present on the source.
087         * <p>A directly present annotation is one that the user has explicitly
088         * declared and not one that is {@linkplain #isMetaPresent() meta-present}
089         * or {@link Inherited @Inherited}.
090         * @return {@code true} if the annotation is directly present
091         */
092        boolean isDirectlyPresent();
093
094        /**
095         * Determine if the annotation is meta-present on the source.
096         * <p>A meta-present annotation is an annotation that the user hasn't
097         * explicitly declared, but has been used as a meta-annotation somewhere in
098         * the annotation hierarchy.
099         * @return {@code true} if the annotation is meta-present
100         */
101        boolean isMetaPresent();
102
103        /**
104         * Get the distance of this annotation related to its use as a
105         * meta-annotation.
106         * <p>A directly declared annotation has a distance of {@code 0}, a
107         * meta-annotation has a distance of {@code 1}, a meta-annotation on a
108         * meta-annotation has a distance of {@code 2}, etc. A {@linkplain #missing()
109         * missing} annotation will always return a distance of {@code -1}.
110         * @return the annotation distance or {@code -1} if the annotation is missing
111         */
112        int getDistance();
113
114        /**
115         * Get the index of the aggregate collection containing this annotation.
116         * <p>Can be used to reorder a stream of annotations, for example, to give a
117         * higher priority to annotations declared on a superclass or interface. A
118         * {@linkplain #missing() missing} annotation will always return an aggregate
119         * index of {@code -1}.
120         * @return the aggregate index (starting at {@code 0}) or {@code -1} if the
121         * annotation is missing
122         */
123        int getAggregateIndex();
124
125        /**
126         * Get the source that ultimately declared the root annotation, or
127         * {@code null} if the source is not known.
128         * <p>If this merged annotation was created
129         * {@link MergedAnnotations#from(AnnotatedElement) from} an
130         * {@link AnnotatedElement} then this source will be an element of the same
131         * type. If the annotation was loaded without using reflection, the source
132         * can be of any type, but should have a sensible {@code toString()}.
133         * Meta-annotations will always return the same source as the
134         * {@link #getRoot() root}.
135         * @return the source, or {@code null}
136         */
137        @Nullable
138        Object getSource();
139
140        /**
141         * Get the source of the meta-annotation, or {@code null} if the
142         * annotation is not {@linkplain #isMetaPresent() meta-present}.
143         * <p>The meta-source is the annotation that was meta-annotated with this
144         * annotation.
145         * @return the meta-annotation source or {@code null}
146         * @see #getRoot()
147         */
148        @Nullable
149        MergedAnnotation<?> getMetaSource();
150
151        /**
152         * Get the root annotation, i.e. the {@link #getDistance() distance} {@code 0}
153         * annotation as directly declared on the source.
154         * @return the root annotation
155         * @see #getMetaSource()
156         */
157        MergedAnnotation<?> getRoot();
158
159        /**
160         * Get the complete list of annotation types within the annotation hierarchy
161         * from this annotation to the {@link #getRoot() root}.
162         * <p>Provides a useful way to uniquely identify a merged annotation instance.
163         * @return the meta types for the annotation
164         * @see MergedAnnotationPredicates#unique(Function)
165         * @see #getRoot()
166         * @see #getMetaSource()
167         */
168        List<Class<? extends Annotation>> getMetaTypes();
169
170
171        /**
172         * Determine if the specified attribute name has a non-default value when
173         * compared to the annotation declaration.
174         * @param attributeName the attribute name
175         * @return {@code true} if the attribute value is different from the default
176         * value
177         */
178        boolean hasNonDefaultValue(String attributeName);
179
180        /**
181         * Determine if the specified attribute name has a default value when compared
182         * to the annotation declaration.
183         * @param attributeName the attribute name
184         * @return {@code true} if the attribute value is the same as the default
185         * value
186         */
187        boolean hasDefaultValue(String attributeName) throws NoSuchElementException;
188
189        /**
190         * Get a required byte attribute value from the annotation.
191         * @param attributeName the attribute name
192         * @return the value as a byte
193         * @throws NoSuchElementException if there is no matching attribute
194         */
195        byte getByte(String attributeName) throws NoSuchElementException;
196
197        /**
198         * Get a required byte array attribute value from the annotation.
199         * @param attributeName the attribute name
200         * @return the value as a byte array
201         * @throws NoSuchElementException if there is no matching attribute
202         */
203        byte[] getByteArray(String attributeName) throws NoSuchElementException;
204
205        /**
206         * Get a required boolean attribute value from the annotation.
207         * @param attributeName the attribute name
208         * @return the value as a boolean
209         * @throws NoSuchElementException if there is no matching attribute
210         */
211        boolean getBoolean(String attributeName) throws NoSuchElementException;
212
213        /**
214         * Get a required boolean array attribute value from the annotation.
215         * @param attributeName the attribute name
216         * @return the value as a boolean array
217         * @throws NoSuchElementException if there is no matching attribute
218         */
219        boolean[] getBooleanArray(String attributeName) throws NoSuchElementException;
220
221        /**
222         * Get a required char attribute value from the annotation.
223         * @param attributeName the attribute name
224         * @return the value as a char
225         * @throws NoSuchElementException if there is no matching attribute
226         */
227        char getChar(String attributeName) throws NoSuchElementException;
228
229        /**
230         * Get a required char array attribute value from the annotation.
231         * @param attributeName the attribute name
232         * @return the value as a char array
233         * @throws NoSuchElementException if there is no matching attribute
234         */
235        char[] getCharArray(String attributeName) throws NoSuchElementException;
236
237        /**
238         * Get a required short attribute value from the annotation.
239         * @param attributeName the attribute name
240         * @return the value as a short
241         * @throws NoSuchElementException if there is no matching attribute
242         */
243        short getShort(String attributeName) throws NoSuchElementException;
244
245        /**
246         * Get a required short array attribute value from the annotation.
247         * @param attributeName the attribute name
248         * @return the value as a short array
249         * @throws NoSuchElementException if there is no matching attribute
250         */
251        short[] getShortArray(String attributeName) throws NoSuchElementException;
252
253        /**
254         * Get a required int attribute value from the annotation.
255         * @param attributeName the attribute name
256         * @return the value as an int
257         * @throws NoSuchElementException if there is no matching attribute
258         */
259        int getInt(String attributeName) throws NoSuchElementException;
260
261        /**
262         * Get a required int array attribute value from the annotation.
263         * @param attributeName the attribute name
264         * @return the value as an int array
265         * @throws NoSuchElementException if there is no matching attribute
266         */
267        int[] getIntArray(String attributeName) throws NoSuchElementException;
268
269        /**
270         * Get a required long attribute value from the annotation.
271         * @param attributeName the attribute name
272         * @return the value as a long
273         * @throws NoSuchElementException if there is no matching attribute
274         */
275        long getLong(String attributeName) throws NoSuchElementException;
276
277        /**
278         * Get a required long array attribute value from the annotation.
279         * @param attributeName the attribute name
280         * @return the value as a long array
281         * @throws NoSuchElementException if there is no matching attribute
282         */
283        long[] getLongArray(String attributeName) throws NoSuchElementException;
284
285        /**
286         * Get a required double attribute value from the annotation.
287         * @param attributeName the attribute name
288         * @return the value as a double
289         * @throws NoSuchElementException if there is no matching attribute
290         */
291        double getDouble(String attributeName) throws NoSuchElementException;
292
293        /**
294         * Get a required double array attribute value from the annotation.
295         * @param attributeName the attribute name
296         * @return the value as a double array
297         * @throws NoSuchElementException if there is no matching attribute
298         */
299        double[] getDoubleArray(String attributeName) throws NoSuchElementException;
300
301        /**
302         * Get a required float attribute value from the annotation.
303         * @param attributeName the attribute name
304         * @return the value as a float
305         * @throws NoSuchElementException if there is no matching attribute
306         */
307        float getFloat(String attributeName) throws NoSuchElementException;
308
309        /**
310         * Get a required float array attribute value from the annotation.
311         * @param attributeName the attribute name
312         * @return the value as a float array
313         * @throws NoSuchElementException if there is no matching attribute
314         */
315        float[] getFloatArray(String attributeName) throws NoSuchElementException;
316
317        /**
318         * Get a required string attribute value from the annotation.
319         * @param attributeName the attribute name
320         * @return the value as a string
321         * @throws NoSuchElementException if there is no matching attribute
322         */
323        String getString(String attributeName) throws NoSuchElementException;
324
325        /**
326         * Get a required string array attribute value from the annotation.
327         * @param attributeName the attribute name
328         * @return the value as a string array
329         * @throws NoSuchElementException if there is no matching attribute
330         */
331        String[] getStringArray(String attributeName) throws NoSuchElementException;
332
333        /**
334         * Get a required class attribute value from the annotation.
335         * @param attributeName the attribute name
336         * @return the value as a class
337         * @throws NoSuchElementException if there is no matching attribute
338         */
339        Class<?> getClass(String attributeName) throws NoSuchElementException;
340
341        /**
342         * Get a required class array attribute value from the annotation.
343         * @param attributeName the attribute name
344         * @return the value as a class array
345         * @throws NoSuchElementException if there is no matching attribute
346         */
347        Class<?>[] getClassArray(String attributeName) throws NoSuchElementException;
348
349        /**
350         * Get a required enum attribute value from the annotation.
351         * @param attributeName the attribute name
352         * @param type the enum type
353         * @return the value as a enum
354         * @throws NoSuchElementException if there is no matching attribute
355         */
356        <E extends Enum<E>> E getEnum(String attributeName, Class<E> type) throws NoSuchElementException;
357
358        /**
359         * Get a required enum array attribute value from the annotation.
360         * @param attributeName the attribute name
361         * @param type the enum type
362         * @return the value as a enum array
363         * @throws NoSuchElementException if there is no matching attribute
364         */
365        <E extends Enum<E>> E[] getEnumArray(String attributeName, Class<E> type) throws NoSuchElementException;
366
367        /**
368         * Get a required annotation attribute value from the annotation.
369         * @param attributeName the attribute name
370         * @param type the annotation type
371         * @return the value as a {@link MergedAnnotation}
372         * @throws NoSuchElementException if there is no matching attribute
373         */
374        <T extends Annotation> MergedAnnotation<T> getAnnotation(String attributeName, Class<T> type)
375                        throws NoSuchElementException;
376
377        /**
378         * Get a required annotation array attribute value from the annotation.
379         * @param attributeName the attribute name
380         * @param type the annotation type
381         * @return the value as a {@link MergedAnnotation} array
382         * @throws NoSuchElementException if there is no matching attribute
383         */
384        <T extends Annotation> MergedAnnotation<T>[] getAnnotationArray(String attributeName, Class<T> type)
385                        throws NoSuchElementException;
386
387        /**
388         * Get an optional attribute value from the annotation.
389         * @param attributeName the attribute name
390         * @return an optional value or {@link Optional#empty()} if there is no
391         * matching attribute
392         */
393        Optional<Object> getValue(String attributeName);
394
395        /**
396         * Get an optional attribute value from the annotation.
397         * @param attributeName the attribute name
398         * @param type the attribute type. Must be compatible with the underlying
399         * attribute type or {@code Object.class}.
400         * @return an optional value or {@link Optional#empty()} if there is no
401         * matching attribute
402         */
403        <T> Optional<T> getValue(String attributeName, Class<T> type);
404
405        /**
406         * Get the default attribute value from the annotation as specified in
407         * the annotation declaration.
408         * @param attributeName the attribute name
409         * @return an optional of the default value or {@link Optional#empty()} if
410         * there is no matching attribute or no defined default
411         */
412        Optional<Object> getDefaultValue(String attributeName);
413
414        /**
415         * Get the default attribute value from the annotation as specified in
416         * the annotation declaration.
417         * @param attributeName the attribute name
418         * @param type the attribute type. Must be compatible with the underlying
419         * attribute type or {@code Object.class}.
420         * @return an optional of the default value or {@link Optional#empty()} if
421         * there is no matching attribute or no defined default
422         */
423        <T> Optional<T> getDefaultValue(String attributeName, Class<T> type);
424
425        /**
426         * Create a new view of the annotation with all attributes that have default
427         * values removed.
428         * @return a filtered view of the annotation without any attributes that
429         * have a default value
430         * @see #filterAttributes(Predicate)
431         */
432        MergedAnnotation<A> filterDefaultValues();
433
434        /**
435         * Create a new view of the annotation with only attributes that match the
436         * given predicate.
437         * @param predicate a predicate used to filter attribute names
438         * @return a filtered view of the annotation
439         * @see #filterDefaultValues()
440         * @see MergedAnnotationPredicates
441         */
442        MergedAnnotation<A> filterAttributes(Predicate<String> predicate);
443
444        /**
445         * Create a new view of the annotation that exposes non-merged attribute values.
446         * <p>Methods from this view will return attribute values with only alias mirroring
447         * rules applied. Aliases to {@link #getMetaSource() meta-source} attributes will
448         * not be applied.
449         * @return a non-merged view of the annotation
450         */
451        MergedAnnotation<A> withNonMergedAttributes();
452
453        /**
454         * Create a new mutable {@link AnnotationAttributes} instance from this
455         * merged annotation.
456         * <p>The {@link Adapt adaptations} may be used to change the way that values
457         * are added.
458         * @param adaptations the adaptations that should be applied to the annotation values
459         * @return an immutable map containing the attributes and values
460         */
461        AnnotationAttributes asAnnotationAttributes(Adapt... adaptations);
462
463        /**
464         * Get an immutable {@link Map} that contains all the annotation attributes.
465         * <p>The {@link Adapt adaptations} may be used to change the way that values are added.
466         * @param adaptations the adaptations that should be applied to the annotation values
467         * @return an immutable map containing the attributes and values
468         */
469        Map<String, Object> asMap(Adapt... adaptations);
470
471        /**
472         * Create a new {@link Map} instance of the given type that contains all the annotation
473         * attributes.
474         * <p>The {@link Adapt adaptations} may be used to change the way that values are added.
475         * @param factory a map factory
476         * @param adaptations the adaptations that should be applied to the annotation values
477         * @return a map containing the attributes and values
478         */
479        <T extends Map<String, Object>> T asMap(Function<MergedAnnotation<?>, T> factory, Adapt... adaptations);
480
481        /**
482         * Create a type-safe synthesized version of this merged annotation that can
483         * be used directly in code.
484         * <p>The result is synthesized using a JDK {@link Proxy} and as a result may
485         * incur a computational cost when first invoked.
486         * <p>If this merged annotation was created {@linkplain #from(Annotation) from}
487         * an annotation instance, that annotation will be returned unmodified if it is
488         * not <em>synthesizable</em>. An annotation is considered synthesizable if
489         * one of the following is true.
490         * <ul>
491         * <li>The annotation declares attributes annotated with {@link AliasFor @AliasFor}.</li>
492         * <li>The annotation is a composed annotation that relies on convention-based
493         * annotation attribute overrides in meta-annotations.</li>
494         * <li>The annotation declares attributes that are annotations or arrays of
495         * annotations that are themselves synthesizable.</li>
496         * </ul>
497         * @return a synthesized version of the annotation or the original annotation
498         * unmodified
499         * @throws NoSuchElementException on a missing annotation
500         */
501        A synthesize() throws NoSuchElementException;
502
503        /**
504         * Optionally create a type-safe synthesized version of this annotation based
505         * on a condition predicate.
506         * <p>The result is synthesized using a JDK {@link Proxy} and as a result may
507         * incur a computational cost when first invoked.
508         * <p>Consult the documentation for {@link #synthesize()} for an explanation
509         * of what is considered synthesizable.
510         * @param condition the test to determine if the annotation can be synthesized
511         * @return an optional containing the synthesized version of the annotation or
512         * an empty optional if the condition doesn't match
513         * @throws NoSuchElementException on a missing annotation
514         * @see MergedAnnotationPredicates
515         */
516        Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition) throws NoSuchElementException;
517
518
519        /**
520         * Create a {@link MergedAnnotation} that represents a missing annotation
521         * (i.e. one that is not present).
522         * @return an instance representing a missing annotation
523         */
524        static <A extends Annotation> MergedAnnotation<A> missing() {
525                return MissingMergedAnnotation.getInstance();
526        }
527
528        /**
529         * Create a new {@link MergedAnnotation} instance from the specified
530         * annotation.
531         * @param annotation the annotation to include
532         * @return a {@link MergedAnnotation} instance containing the annotation
533         */
534        static <A extends Annotation> MergedAnnotation<A> from(A annotation) {
535                return from(null, annotation);
536        }
537
538        /**
539         * Create a new {@link MergedAnnotation} instance from the specified
540         * annotation.
541         * @param source the source for the annotation. This source is used only for
542         * information and logging. It does not need to <em>actually</em> contain
543         * the specified annotations, and it will not be searched.
544         * @param annotation the annotation to include
545         * @return a {@link MergedAnnotation} instance for the annotation
546         */
547        static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
548                return TypeMappedAnnotation.from(source, annotation);
549        }
550
551        /**
552         * Create a new {@link MergedAnnotation} instance of the specified
553         * annotation type. The resulting annotation will not have any attribute
554         * values but may still be used to query default values.
555         * @param annotationType the annotation type
556         * @return a {@link MergedAnnotation} instance for the annotation
557         */
558        static <A extends Annotation> MergedAnnotation<A> of(Class<A> annotationType) {
559                return of(null, annotationType, null);
560        }
561
562        /**
563         * Create a new {@link MergedAnnotation} instance of the specified
564         * annotation type with attribute values supplied by a map.
565         * @param annotationType the annotation type
566         * @param attributes the annotation attributes or {@code null} if just default
567         * values should be used
568         * @return a {@link MergedAnnotation} instance for the annotation and attributes
569         * @see #of(AnnotatedElement, Class, Map)
570         */
571        static <A extends Annotation> MergedAnnotation<A> of(
572                        Class<A> annotationType, @Nullable Map<String, ?> attributes) {
573
574                return of(null, annotationType, attributes);
575        }
576
577        /**
578         * Create a new {@link MergedAnnotation} instance of the specified
579         * annotation type with attribute values supplied by a map.
580         * @param source the source for the annotation. This source is used only for
581         * information and logging. It does not need to <em>actually</em> contain
582         * the specified annotations and it will not be searched.
583         * @param annotationType the annotation type
584         * @param attributes the annotation attributes or {@code null} if just default
585         * values should be used
586         * @return a {@link MergedAnnotation} instance for the annotation and attributes
587         */
588        static <A extends Annotation> MergedAnnotation<A> of(
589                        @Nullable AnnotatedElement source, Class<A> annotationType, @Nullable Map<String, ?> attributes) {
590
591                return of(null, source, annotationType, attributes);
592        }
593
594        /**
595         * Create a new {@link MergedAnnotation} instance of the specified
596         * annotation type with attribute values supplied by a map.
597         * @param classLoader the class loader used to resolve class attributes
598         * @param source the source for the annotation. This source is used only for
599         * information and logging. It does not need to <em>actually</em> contain
600         * the specified annotations and it will not be searched.
601         * @param annotationType the annotation type
602         * @param attributes the annotation attributes or {@code null} if just default
603         * values should be used
604         * @return a {@link MergedAnnotation} instance for the annotation and attributes
605         */
606        static <A extends Annotation> MergedAnnotation<A> of(
607                        @Nullable ClassLoader classLoader, @Nullable Object source,
608                        Class<A> annotationType, @Nullable Map<String, ?> attributes) {
609
610                return TypeMappedAnnotation.of(classLoader, source, annotationType, attributes);
611        }
612
613
614        /**
615         * Adaptations that can be applied to attribute values when creating
616         * {@linkplain MergedAnnotation#asMap(Adapt...) Maps} or
617         * {@link MergedAnnotation#asAnnotationAttributes(Adapt...) AnnotationAttributes}.
618         */
619        enum Adapt {
620
621                /**
622                 * Adapt class or class array attributes to strings.
623                 */
624                CLASS_TO_STRING,
625
626                /**
627                 * Adapt nested annotation or annotation arrays to maps rather
628                 * than synthesizing the values.
629                 */
630                ANNOTATION_TO_MAP;
631
632                protected final boolean isIn(Adapt... adaptations) {
633                        for (Adapt candidate : adaptations) {
634                                if (candidate == this) {
635                                        return true;
636                                }
637                        }
638                        return false;
639                }
640
641                /**
642                 * Factory method to create an {@link Adapt} array from a set of boolean flags.
643                 * @param classToString if {@link Adapt#CLASS_TO_STRING} is included
644                 * @param annotationsToMap if {@link Adapt#ANNOTATION_TO_MAP} is included
645                 * @return a new {@link Adapt} array
646                 */
647                public static Adapt[] values(boolean classToString, boolean annotationsToMap) {
648                        EnumSet<Adapt> result = EnumSet.noneOf(Adapt.class);
649                        addIfTrue(result, Adapt.CLASS_TO_STRING, classToString);
650                        addIfTrue(result, Adapt.ANNOTATION_TO_MAP, annotationsToMap);
651                        return result.toArray(new Adapt[0]);
652                }
653
654                private static <T> void addIfTrue(Set<T> result, T value, boolean test) {
655                        if (test) {
656                                result.add(value);
657                        }
658                }
659        }
660
661}