001/*
002 * Copyright 2002-2015 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.classreading;
018
019import java.util.Collection;
020import java.util.LinkedHashMap;
021import java.util.LinkedHashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025
026import org.springframework.asm.AnnotationVisitor;
027import org.springframework.asm.MethodVisitor;
028import org.springframework.asm.Opcodes;
029import org.springframework.asm.Type;
030import org.springframework.core.annotation.AnnotationAttributes;
031import org.springframework.core.annotation.AnnotationUtils;
032import org.springframework.core.type.AnnotationMetadata;
033import org.springframework.core.type.MethodMetadata;
034import org.springframework.util.LinkedMultiValueMap;
035import org.springframework.util.MultiValueMap;
036
037/**
038 * ASM class visitor which looks for the class name and implemented types as
039 * well as for the annotations defined on the class, exposing them through
040 * the {@link org.springframework.core.type.AnnotationMetadata} interface.
041 *
042 * @author Juergen Hoeller
043 * @author Mark Fisher
044 * @author Costin Leau
045 * @author Phillip Webb
046 * @author Sam Brannen
047 * @since 2.5
048 */
049public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
050
051        protected final ClassLoader classLoader;
052
053        protected final Set<String> annotationSet = new LinkedHashSet<String>(4);
054
055        protected final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>(4);
056
057        /**
058         * Declared as a {@link LinkedMultiValueMap} instead of a {@link MultiValueMap}
059         * to ensure that the hierarchical ordering of the entries is preserved.
060         * @see AnnotationReadingVisitorUtils#getMergedAnnotationAttributes
061         */
062        protected final LinkedMultiValueMap<String, AnnotationAttributes> attributesMap =
063                        new LinkedMultiValueMap<String, AnnotationAttributes>(4);
064
065        protected final Set<MethodMetadata> methodMetadataSet = new LinkedHashSet<MethodMetadata>(4);
066
067
068        public AnnotationMetadataReadingVisitor(ClassLoader classLoader) {
069                this.classLoader = classLoader;
070        }
071
072
073        @Override
074        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
075                // Skip bridge methods - we're only interested in original annotation-defining user methods.
076                // On JDK 8, we'd otherwise run into double detection of the same annotated method...
077                if ((access & Opcodes.ACC_BRIDGE) != 0) {
078                        return super.visitMethod(access, name, desc, signature, exceptions);
079                }
080                return new MethodMetadataReadingVisitor(name, access, getClassName(),
081                                Type.getReturnType(desc).getClassName(), this.classLoader, this.methodMetadataSet);
082        }
083
084        @Override
085        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
086                String className = Type.getType(desc).getClassName();
087                this.annotationSet.add(className);
088                return new AnnotationAttributesReadingVisitor(
089                                className, this.attributesMap, this.metaAnnotationMap, this.classLoader);
090        }
091
092
093        @Override
094        public Set<String> getAnnotationTypes() {
095                return this.annotationSet;
096        }
097
098        @Override
099        public Set<String> getMetaAnnotationTypes(String annotationName) {
100                return this.metaAnnotationMap.get(annotationName);
101        }
102
103        @Override
104        public boolean hasAnnotation(String annotationName) {
105                return this.annotationSet.contains(annotationName);
106        }
107
108        @Override
109        public boolean hasMetaAnnotation(String metaAnnotationType) {
110                Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
111                for (Set<String> metaTypes : allMetaTypes) {
112                        if (metaTypes.contains(metaAnnotationType)) {
113                                return true;
114                        }
115                }
116                return false;
117        }
118
119        @Override
120        public boolean isAnnotated(String annotationName) {
121                return (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName) &&
122                                this.attributesMap.containsKey(annotationName));
123        }
124
125        @Override
126        public AnnotationAttributes getAnnotationAttributes(String annotationName) {
127                return getAnnotationAttributes(annotationName, false);
128        }
129
130        @Override
131        public AnnotationAttributes getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
132                AnnotationAttributes raw = AnnotationReadingVisitorUtils.getMergedAnnotationAttributes(
133                                this.attributesMap, this.metaAnnotationMap, annotationName);
134                return AnnotationReadingVisitorUtils.convertClassValues(
135                                "class '" + getClassName() + "'", this.classLoader, raw, classValuesAsString);
136        }
137
138        @Override
139        public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) {
140                return getAllAnnotationAttributes(annotationName, false);
141        }
142
143        @Override
144        public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
145                MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<String, Object>();
146                List<AnnotationAttributes> attributes = this.attributesMap.get(annotationName);
147                if (attributes == null) {
148                        return null;
149                }
150                for (AnnotationAttributes raw : attributes) {
151                        for (Map.Entry<String, Object> entry : AnnotationReadingVisitorUtils.convertClassValues(
152                                        "class '" + getClassName() + "'", this.classLoader, raw, classValuesAsString).entrySet()) {
153                                allAttributes.add(entry.getKey(), entry.getValue());
154                        }
155                }
156                return allAttributes;
157        }
158
159        @Override
160        public boolean hasAnnotatedMethods(String annotationName) {
161                for (MethodMetadata methodMetadata : this.methodMetadataSet) {
162                        if (methodMetadata.isAnnotated(annotationName)) {
163                                return true;
164                        }
165                }
166                return false;
167        }
168
169        @Override
170        public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
171                Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>(4);
172                for (MethodMetadata methodMetadata : this.methodMetadataSet) {
173                        if (methodMetadata.isAnnotated(annotationName)) {
174                                annotatedMethods.add(methodMetadata);
175                        }
176                }
177                return annotatedMethods;
178        }
179
180}