001/*
002 * Copyright 2002-2019 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.type;
018
019import java.util.Collections;
020import java.util.LinkedHashSet;
021import java.util.Set;
022import java.util.stream.Collectors;
023
024import org.springframework.core.annotation.MergedAnnotation;
025import org.springframework.core.annotation.MergedAnnotations;
026import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
027
028/**
029 * Interface that defines abstract access to the annotations of a specific
030 * class, in a form that does not require that class to be loaded yet.
031 *
032 * @author Juergen Hoeller
033 * @author Mark Fisher
034 * @author Phillip Webb
035 * @author Sam Brannen
036 * @since 2.5
037 * @see StandardAnnotationMetadata
038 * @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata()
039 * @see AnnotatedTypeMetadata
040 */
041public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
042
043        /**
044         * Get the fully qualified class names of all annotation types that
045         * are <em>present</em> on the underlying class.
046         * @return the annotation type names
047         */
048        default Set<String> getAnnotationTypes() {
049                return getAnnotations().stream()
050                                .filter(MergedAnnotation::isDirectlyPresent)
051                                .map(annotation -> annotation.getType().getName())
052                                .collect(Collectors.toCollection(LinkedHashSet::new));
053        }
054
055        /**
056         * Get the fully qualified class names of all meta-annotation types that
057         * are <em>present</em> on the given annotation type on the underlying class.
058         * @param annotationName the fully qualified class name of the meta-annotation
059         * type to look for
060         * @return the meta-annotation type names, or an empty set if none found
061         */
062        default Set<String> getMetaAnnotationTypes(String annotationName) {
063                MergedAnnotation<?> annotation = getAnnotations().get(annotationName, MergedAnnotation::isDirectlyPresent);
064                if (!annotation.isPresent()) {
065                        return Collections.emptySet();
066                }
067                return MergedAnnotations.from(annotation.getType(), SearchStrategy.INHERITED_ANNOTATIONS).stream()
068                                .map(mergedAnnotation -> mergedAnnotation.getType().getName())
069                                .collect(Collectors.toCollection(LinkedHashSet::new));
070        }
071
072        /**
073         * Determine whether an annotation of the given type is <em>present</em> on
074         * the underlying class.
075         * @param annotationName the fully qualified class name of the annotation
076         * type to look for
077         * @return {@code true} if a matching annotation is present
078         */
079        default boolean hasAnnotation(String annotationName) {
080                return getAnnotations().isDirectlyPresent(annotationName);
081        }
082
083        /**
084         * Determine whether the underlying class has an annotation that is itself
085         * annotated with the meta-annotation of the given type.
086         * @param metaAnnotationName the fully qualified class name of the
087         * meta-annotation type to look for
088         * @return {@code true} if a matching meta-annotation is present
089         */
090        default boolean hasMetaAnnotation(String metaAnnotationName) {
091                return getAnnotations().get(metaAnnotationName,
092                                MergedAnnotation::isMetaPresent).isPresent();
093        }
094
095        /**
096         * Determine whether the underlying class has any methods that are
097         * annotated (or meta-annotated) with the given annotation type.
098         * @param annotationName the fully qualified class name of the annotation
099         * type to look for
100         */
101        default boolean hasAnnotatedMethods(String annotationName) {
102                return !getAnnotatedMethods(annotationName).isEmpty();
103        }
104
105        /**
106         * Retrieve the method metadata for all methods that are annotated
107         * (or meta-annotated) with the given annotation type.
108         * <p>For any returned method, {@link MethodMetadata#isAnnotated} will
109         * return {@code true} for the given annotation type.
110         * @param annotationName the fully qualified class name of the annotation
111         * type to look for
112         * @return a set of {@link MethodMetadata} for methods that have a matching
113         * annotation. The return value will be an empty set if no methods match
114         * the annotation type.
115         */
116        Set<MethodMetadata> getAnnotatedMethods(String annotationName);
117
118
119        /**
120         * Factory method to create a new {@link AnnotationMetadata} instance
121         * for the given class using standard reflection.
122         * @param type the class to introspect
123         * @return a new {@link AnnotationMetadata} instance
124         * @since 5.2
125         */
126        static AnnotationMetadata introspect(Class<?> type) {
127                return StandardAnnotationMetadata.from(type);
128        }
129
130}