1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.annotationvisitor; 18 19 import org.apache.bcel.classfile.AnnotationEntry; 20 import org.apache.bcel.classfile.DescendingVisitor; 21 import org.apache.bcel.classfile.EmptyVisitor; 22 import org.apache.bcel.classfile.Field; 23 import org.apache.bcel.classfile.FieldOrMethod; 24 import org.apache.bcel.classfile.JavaClass; 25 import org.apache.bcel.classfile.Method; 26 27 import java.util.Map; 28 29 /** 30 * Visits a JavaClass instance and passes any annotated members to a {@link AnnotationHandler} 31 * according to the map provided. 32 */ 33 public class AnnotationVisitor extends EmptyVisitor { 34 35 private final JavaClass mClass; 36 private final Status mStatus; 37 private final DescendingVisitor mDescendingVisitor; 38 private final Map<String, AnnotationHandler> mAnnotationHandlers; 39 40 /** 41 * Creates a visitor for a class. 42 * 43 * @param clazz Class to visit 44 * @param status For reporting debug information 45 * @param handlers Map of {@link AnnotationHandler}. The keys should be annotation names, as 46 * class descriptors. 47 */ AnnotationVisitor(JavaClass clazz, Status status, Map<String, AnnotationHandler> handlers)48 public AnnotationVisitor(JavaClass clazz, Status status, 49 Map<String, AnnotationHandler> handlers) { 50 mClass = clazz; 51 mStatus = status; 52 mAnnotationHandlers = handlers; 53 mDescendingVisitor = new DescendingVisitor(clazz, this); 54 } 55 visit()56 public void visit() { 57 mStatus.debug("Visit class %s", mClass.getClassName()); 58 AnnotationContext context = new AnnotatedClassContext(mStatus, mClass, "L%s;"); 59 AnnotationEntry[] annotationEntries = mClass.getAnnotationEntries(); 60 handleAnnotations(context, annotationEntries); 61 62 mDescendingVisitor.visit(); 63 } 64 65 @Override visitMethod(Method method)66 public void visitMethod(Method method) { 67 visitMember(method, "L%s;->%s%s"); 68 } 69 70 @Override visitField(Field field)71 public void visitField(Field field) { 72 visitMember(field, "L%s;->%s:%s"); 73 } 74 visitMember(FieldOrMethod member, String signatureFormatString)75 private void visitMember(FieldOrMethod member, String signatureFormatString) { 76 mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature()); 77 AnnotationContext context = new AnnotatedMemberContext(mStatus, 78 (JavaClass) mDescendingVisitor.predecessor(), member, 79 signatureFormatString); 80 AnnotationEntry[] annotationEntries = member.getAnnotationEntries(); 81 handleAnnotations(context, annotationEntries); 82 } 83 handleAnnotations(AnnotationContext context, AnnotationEntry[] annotationEntries)84 private void handleAnnotations(AnnotationContext context, AnnotationEntry[] annotationEntries) { 85 for (AnnotationEntry a : annotationEntries) { 86 if (mAnnotationHandlers.containsKey(a.getAnnotationType())) { 87 mStatus.debug("Member has annotation %s for which we have a handler", 88 a.getAnnotationType()); 89 mAnnotationHandlers.get(a.getAnnotationType()).handleAnnotation(a, context); 90 } else { 91 mStatus.debug("Member has annotation %s for which we do not have a handler", 92 a.getAnnotationType()); 93 } 94 } 95 } 96 } 97