/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.annotationvisitor; import org.apache.bcel.classfile.AnnotationEntry; import org.apache.bcel.classfile.DescendingVisitor; import org.apache.bcel.classfile.EmptyVisitor; import org.apache.bcel.classfile.Field; import org.apache.bcel.classfile.FieldOrMethod; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; import java.util.Map; /** * Visits a JavaClass instance and passes any annotated members to a {@link AnnotationHandler} * according to the map provided. */ public class AnnotationVisitor extends EmptyVisitor { private final JavaClass mClass; private final Status mStatus; private final DescendingVisitor mDescendingVisitor; private final Map mAnnotationHandlers; /** * Creates a visitor for a class. * * @param clazz Class to visit * @param status For reporting debug information * @param handlers Map of {@link AnnotationHandler}. The keys should be annotation names, as * class descriptors. */ public AnnotationVisitor(JavaClass clazz, Status status, Map handlers) { mClass = clazz; mStatus = status; mAnnotationHandlers = handlers; mDescendingVisitor = new DescendingVisitor(clazz, this); } public void visit() { mStatus.debug("Visit class %s", mClass.getClassName()); AnnotationContext context = new AnnotatedClassContext(mStatus, mClass, "L%s;"); AnnotationEntry[] annotationEntries = mClass.getAnnotationEntries(); handleAnnotations(context, annotationEntries); mDescendingVisitor.visit(); } @Override public void visitMethod(Method method) { visitMember(method, "L%s;->%s%s"); } @Override public void visitField(Field field) { visitMember(field, "L%s;->%s:%s"); } private void visitMember(FieldOrMethod member, String signatureFormatString) { mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature()); AnnotationContext context = new AnnotatedMemberContext(mStatus, (JavaClass) mDescendingVisitor.predecessor(), member, signatureFormatString); AnnotationEntry[] annotationEntries = member.getAnnotationEntries(); handleAnnotations(context, annotationEntries); } private void handleAnnotations(AnnotationContext context, AnnotationEntry[] annotationEntries) { for (AnnotationEntry a : annotationEntries) { if (mAnnotationHandlers.containsKey(a.getAnnotationType())) { mStatus.debug("Member has annotation %s for which we have a handler", a.getAnnotationType()); mAnnotationHandlers.get(a.getAnnotationType()).handleAnnotation(a, context); } else { mStatus.debug("Member has annotation %s for which we do not have a handler", a.getAnnotationType()); } } } }