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