001/*
002 * Copyright 2002-2017 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.lang.annotation.Annotation;
020import java.lang.reflect.Method;
021import java.util.LinkedHashSet;
022import java.util.Map;
023import java.util.Set;
024
025import org.springframework.core.annotation.AnnotatedElementUtils;
026import org.springframework.util.MultiValueMap;
027
028/**
029 * {@link AnnotationMetadata} implementation that uses standard reflection
030 * to introspect a given {@link Class}.
031 *
032 * @author Juergen Hoeller
033 * @author Mark Fisher
034 * @author Chris Beams
035 * @author Phillip Webb
036 * @author Sam Brannen
037 * @since 2.5
038 */
039public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
040
041        private final Annotation[] annotations;
042
043        private final boolean nestedAnnotationsAsMap;
044
045
046        /**
047         * Create a new {@code StandardAnnotationMetadata} wrapper for the given Class.
048         * @param introspectedClass the Class to introspect
049         * @see #StandardAnnotationMetadata(Class, boolean)
050         */
051        public StandardAnnotationMetadata(Class<?> introspectedClass) {
052                this(introspectedClass, false);
053        }
054
055        /**
056         * Create a new {@link StandardAnnotationMetadata} wrapper for the given Class,
057         * providing the option to return any nested annotations or annotation arrays in the
058         * form of {@link org.springframework.core.annotation.AnnotationAttributes} instead
059         * of actual {@link Annotation} instances.
060         * @param introspectedClass the Class to introspect
061         * @param nestedAnnotationsAsMap return nested annotations and annotation arrays as
062         * {@link org.springframework.core.annotation.AnnotationAttributes} for compatibility
063         * with ASM-based {@link AnnotationMetadata} implementations
064         * @since 3.1.1
065         */
066        public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
067                super(introspectedClass);
068                this.annotations = introspectedClass.getAnnotations();
069                this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
070        }
071
072
073        @Override
074        public Set<String> getAnnotationTypes() {
075                Set<String> types = new LinkedHashSet<String>();
076                for (Annotation ann : this.annotations) {
077                        types.add(ann.annotationType().getName());
078                }
079                return types;
080        }
081
082        @Override
083        public Set<String> getMetaAnnotationTypes(String annotationName) {
084                return (this.annotations.length > 0 ?
085                                AnnotatedElementUtils.getMetaAnnotationTypes(getIntrospectedClass(), annotationName) : null);
086        }
087
088        @Override
089        public boolean hasAnnotation(String annotationName) {
090                for (Annotation ann : this.annotations) {
091                        if (ann.annotationType().getName().equals(annotationName)) {
092                                return true;
093                        }
094                }
095                return false;
096        }
097
098        @Override
099        public boolean hasMetaAnnotation(String annotationName) {
100                return (this.annotations.length > 0 &&
101                                AnnotatedElementUtils.hasMetaAnnotationTypes(getIntrospectedClass(), annotationName));
102        }
103
104        @Override
105        public boolean isAnnotated(String annotationName) {
106                return (this.annotations.length > 0 &&
107                                AnnotatedElementUtils.isAnnotated(getIntrospectedClass(), annotationName));
108        }
109
110        @Override
111        public Map<String, Object> getAnnotationAttributes(String annotationName) {
112                return getAnnotationAttributes(annotationName, false);
113        }
114
115        @Override
116        public Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
117                return (this.annotations.length > 0 ? AnnotatedElementUtils.getMergedAnnotationAttributes(
118                                getIntrospectedClass(), annotationName, classValuesAsString, this.nestedAnnotationsAsMap) : null);
119        }
120
121        @Override
122        public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) {
123                return getAllAnnotationAttributes(annotationName, false);
124        }
125
126        @Override
127        public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
128                return (this.annotations.length > 0 ? AnnotatedElementUtils.getAllAnnotationAttributes(
129                                getIntrospectedClass(), annotationName, classValuesAsString, this.nestedAnnotationsAsMap) : null);
130        }
131
132        @Override
133        public boolean hasAnnotatedMethods(String annotationName) {
134                try {
135                        Method[] methods = getIntrospectedClass().getDeclaredMethods();
136                        for (Method method : methods) {
137                                if (!method.isBridge() && method.getAnnotations().length > 0 &&
138                                                AnnotatedElementUtils.isAnnotated(method, annotationName)) {
139                                        return true;
140                                }
141                        }
142                        return false;
143                }
144                catch (Throwable ex) {
145                        throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
146                }
147        }
148
149        @Override
150        public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
151                try {
152                        Method[] methods = getIntrospectedClass().getDeclaredMethods();
153                        Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>(4);
154                        for (Method method : methods) {
155                                if (!method.isBridge() && method.getAnnotations().length > 0 &&
156                                                AnnotatedElementUtils.isAnnotated(method, annotationName)) {
157                                        annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
158                                }
159                        }
160                        return annotatedMethods;
161                }
162                catch (Throwable ex) {
163                        throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
164                }
165        }
166
167}