blob: bc09807962288080d8339a16b19ad2c9d9c5c39f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.xbean.finder;
import org.apache.xbean.asm7.original.commons.EmptyVisitor;
import org.apache.xbean.finder.archive.Archive;
import org.apache.xbean.finder.util.Classes;
import org.apache.xbean.finder.util.SingleLinkedList;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* ClassFinder searches the classpath of the specified classloader for
* packages, classes, constructors, methods, or fields with specific annotations.
* <p/>
* For security reasons ASM is used to find the annotations. Classes are not
* loaded unless they match the requirements of a called findAnnotated* method.
* Once loaded, these classes are cached.
*
* @version $Rev$ $Date$
*/
public class AnnotationFinder implements IAnnotationFinder {
private static final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES;
// this flag is just a backdoor to allow workaround in case we impact an application, if we aresafe for 2-3 versions
// let remove it
//
// main issue which can happen is a parent class we dont want to scan appears,
// xbean.finder.prevent-lazy-linking= true will prevent it, see readClassDef(Class)
private static final boolean ALLOW_LAZY_LINKING = !Boolean.getBoolean("xbean.finder.prevent-lazy-linking");
private final Set<Class<? extends Annotation>> metaroots = new HashSet<Class<? extends Annotation>>();
protected final Map<String, List<Info>> annotated = newAnnotatedMap();
protected final Map<String, ClassInfo> classInfos = newClassInfoMap();
protected final Map<String, ClassInfo> originalInfos = newClassInfoMap();
private final List<String> classesNotLoaded = new LinkedList<String>();
private final Archive archive;
private final boolean checkRuntimeAnnotation;
private volatile boolean linking;
private AnnotationFinder(AnnotationFinder parent, Iterable<String> classNames) {
this.archive = new SubArchive(classNames);
this.checkRuntimeAnnotation = parent.checkRuntimeAnnotation;
this.metaroots.addAll(parent.metaroots);
for (Class<? extends Annotation> metaroot : metaroots) {
final ClassInfo info = parent.classInfos.get(metaroot.getName());
if (info == null) continue;
readClassDef(info);
}
for (String name : classNames) {
final ClassInfo info = parent.classInfos.get(name);
if (info == null) continue;
readClassDef(info);
}
resolveAnnotations(parent, new LinkedList<String>());
for (ClassInfo classInfo : classInfos.values()) {
if (isMetaRoot(classInfo)) {
try {
metaroots.add((Class<? extends Annotation>) classInfo.get());
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
}
}
}
for (Class<? extends Annotation> metaroot : metaroots) {
List<Info> infoList = annotated.get(metaroot.getName());
for (Info info : infoList) {
final String className = info.getName() + "$$";
final ClassInfo i = parent.classInfos.get(className);
if (i == null) continue;
readClassDef(i);
}
}
}
protected Map<String, List<Info>> newAnnotatedMap() {
return new HashMap<String, List<Info>>();
}
protected Map<String, ClassInfo> newClassInfoMap() {
return new HashMap<String, ClassInfo>();
}
/**
*
* @param archive
* @param checkRuntimeAnnotation Has no effect on findMetaAnnotated* methods
*/
public AnnotationFinder(Archive archive, boolean checkRuntimeAnnotation) {
this.archive = archive;
this.checkRuntimeAnnotation = checkRuntimeAnnotation;
for (Archive.Entry entry : archive) {
final String className = entry.getName();
try {
readClassDef(entry.getBytecode());
} catch (NoClassDefFoundError e) {
throw new NoClassDefFoundError("Could not fully load class: " + className + "\n due to:" + e.getMessage());
} catch (IOException e) {
e.printStackTrace();
}
}
// keep track of what was originally from the archives
originalInfos.putAll(classInfos);
}
public AnnotationFinder(Archive archive) {
this(archive, true);
}
public boolean hasMetaAnnotations() {
return metaroots.size() > 0;
}
private void readClassDef(ClassInfo info) {
classInfos.put(info.getName(), info);
index(info);
index(info.constructors);
for (MethodInfo ctor : info.constructors) {
index(ctor.parameters);
}
index(info.methods);
for (MethodInfo method : info.methods) {
index(method.parameters);
}
index(info.fields);
}
private void resolveAnnotations(AnnotationFinder parent, List<String> scanned) {
// Get a list of the annotations that exist before we start
final List<String> annotations = new ArrayList<String>(annotated.keySet());
for (String annotation : annotations) {
if (scanned.contains(annotation)) continue;
final ClassInfo info = parent.classInfos.get(annotation);
if (info == null) continue;
readClassDef(info);
}
// If the "annotated" list has grown, then we must scan those
if (annotated.keySet().size() != annotations.size()) {
resolveAnnotations(parent, annotations);
}
}
private void index(List<? extends Info> infos) {
for (Info i : infos) {
index(i);
}
}
private void index(Info i) {
for (AnnotationInfo annotationInfo : i.getAnnotations()) {
index(annotationInfo, i);
}
}
public List<String> getAnnotatedClassNames() {
return new ArrayList<String>(originalInfos.keySet());
}
public Archive getArchive() {
return archive;
}
/**
* The link() method must be called to successfully use the findSubclasses and findImplementations methods
*
* @return
* @throws java.io.IOException
*/
public AnnotationFinder link() {
enableFindSubclasses();
enableFindImplementations();
enableMetaAnnotations();
return this;
}
public AnnotationFinder enableMetaAnnotations() {
// diff new and old lists
resolveAnnotations(new LinkedList<String>());
linkMetaAnnotations();
return this;
}
public AnnotationFinder enableFindImplementations() {
for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
linkInterfaces(classInfo);
}
return this;
}
public AnnotationFinder enableFindSubclasses() {
final boolean originalLinking = linking;
linking = ALLOW_LAZY_LINKING;
for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
linkParent(classInfo);
}
linking = originalLinking;
return this;
}
/**
* Used to support meta annotations
* <p/>
* Once the list of classes has been read from the Archive, we
* iterate over all the annotations that are used by those classes
* and recursively resolve any annotations those annotations use.
*
* @param scanned
* @throws ClassNotFoundException
* @throws IOException
*/
private void resolveAnnotations(List<String> scanned) {
// Get a list of the annotations that exist before we start
final List<String> annotations = new ArrayList<String>(annotated.keySet());
for (String annotation : annotations) {
if (scanned.contains(annotation)) continue;
readClassDef(annotation);
}
// If the "annotated" list has grown, then we must scan those
if (annotated.keySet().size() != annotations.size()) {
resolveAnnotations(annotations);
}
// for (ClassInfo classInfo : classInfos.values()) {
// for (AnnotationInfo annotationInfo : classInfo.getAnnotations()) {
// for (AnnotationInfo info : annotationInfo.getAnnotations()) {
// final String annotation = info.getName();
//
// if (hasName(annotation, "Metaroot") && !scanned.contains(annotation)) {
// readClassDef(annotation);
// }
// }
// }
// }
}
private void linkMetaAnnotations() {
for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
if (isMetaRoot(classInfo)) {
try {
metaroots.add((Class<? extends Annotation>) classInfo.get());
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
}
}
}
for (Class<? extends Annotation> metaroot : metaroots) {
List<Info> infoList = annotated.get(metaroot.getName());
for (Info info : infoList) {
readClassDef(info.getName() + "$$");
}
}
}
private boolean isMetaRoot(ClassInfo classInfo) {
if (!classInfo.isAnnotation()) return false;
if (classInfo.getName().equals("javax.annotation.Metatype")) return true;
if (isSelfAnnotated(classInfo, "Metatype")) return true;
if (isSelfAnnotated(classInfo, "Metaroot")) return false;
for (AnnotationInfo annotationInfo : classInfo.getAnnotations()) {
final ClassInfo annotation = classInfos.get(annotationInfo.getName());
if (annotation == null) return false;
if (annotation.getName().equals("javax.annotation.Metaroot")) return true;
if (isSelfAnnotated(annotation, "Metaroot")) return true;
}
return false;
}
private boolean isSelfAnnotated(ClassInfo classInfo, String metatype) {
if (!classInfo.isAnnotation()) return false;
final String name = classInfo.getName();
if (!hasName(name, metatype)) return false;
for (AnnotationInfo info : classInfo.getAnnotations()) {
if (info.getName().equals(name)) return true;
}
return true;
}
private boolean hasName(String className, String simpleName) {
return className.equals(simpleName) || className.endsWith("." + simpleName) || className.endsWith("$" + simpleName);
}
protected void linkParent(ClassInfo classInfo) {
if (classInfo.superType == null) return;
if (isJvm(classInfo.superType)) return;
ClassInfo parentInfo = classInfo.superclassInfo;
if (parentInfo == null) {
parentInfo = classInfos.get(classInfo.superType);
if (parentInfo == null) {
// best scanning we can do, try it first
readClassDef(classInfo.superType);
parentInfo = classInfos.get(classInfo.superType);
if (parentInfo == null) {
// parentInfo == null means readClassDef fails so clean up error and retry
classesNotLoaded.remove(classInfo.superType);
try {
if (classInfo.get() != null) { // call get() to ensure clazz got a change to be loaded
readClassDef(((Class<?>) classInfo.clazz).getSuperclass());
parentInfo = classInfos.get(classInfo.superType);
}
} catch (final ClassNotFoundException e) {
// no-op
} catch (final Throwable e) {
// no-op
}
}
if (parentInfo == null) return;
linkParent(parentInfo);
}
classInfo.superclassInfo = parentInfo;
}
synchronized (parentInfo.subclassInfos) {
if (!parentInfo.subclassInfos.contains(classInfo)) {
parentInfo.subclassInfos.add(classInfo);
}
}
}
/*
protected boolean isJvm(final String superType) {
// TODO: can't we simply do startsWith("java")?
return superType.startsWith("java.lang.")
|| superType.startsWith("java.beans.")
|| superType.startsWith("java.util.")
|| superType.startsWith("java.io.")
|| superType.startsWith("java.text.")
|| superType.startsWith("java.net.")
|| superType.startsWith("java.sql.")
|| superType.startsWith("java.security.")
|| superType.startsWith("java.awt.")
|| superType.startsWith("javax.swing.");
}
*/
protected boolean isJvm(final String name) {
return name.startsWith("java.");
}
protected void linkInterfaces(ClassInfo classInfo) {
final List<ClassInfo> infos = new LinkedList<ClassInfo>();
if (classInfo.clazz != null) {
final Class<?>[] interfaces = classInfo.clazz.getInterfaces();
for (Class<?> clazz : interfaces) {
ClassInfo interfaceInfo = classInfos.get(clazz.getName());
if (interfaceInfo == null) {
readClassDef(clazz);
}
interfaceInfo = classInfos.get(clazz.getName());
if (interfaceInfo != null) {
infos.add(interfaceInfo);
}
}
} else {
for (final String className : classInfo.interfaces) {
if (isJvm(className)) {
continue;
}
ClassInfo interfaceInfo = classInfos.get(className);
if (interfaceInfo == null) {
readClassDef(className);
}
interfaceInfo = classInfos.get(className);
if (interfaceInfo != null) {
infos.add(interfaceInfo);
}
}
}
for (ClassInfo info : infos) {
linkInterfaces(info);
}
}
public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
List<Info> infos = annotated.get(annotation.getName());
return infos != null && !infos.isEmpty();
}
/**
* Returns a list of classes that could not be loaded in last invoked findAnnotated* method.
* <p/>
* The list will only contain entries of classes whose byte code matched the requirements
* of last invoked find* method, but were unable to be loaded and included in the results.
* <p/>
* The list returned is unmodifiable. Once obtained, the returned list will be a live view of the
* results from the last findAnnotated* method call.
* <p/>
* This method is not thread safe.
*
* @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call.
*/
public List<String> getClassesNotLoaded() {
return Collections.unmodifiableList(classesNotLoaded);
}
public List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
List<Package> packages = new LinkedList<Package>();
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
if (info instanceof PackageInfo) {
PackageInfo packageInfo = (PackageInfo) info;
try {
Package pkg = packageInfo.get();
// double check via proper reflection
if (!checkRuntimeAnnotation || pkg.isAnnotationPresent(annotation)) {
packages.add(pkg);
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(packageInfo.getName());
}
}
}
return packages;
}
public List<Class<?>> findAnnotatedClasses(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
List<Class<?>> classes = new LinkedList<Class<?>>();
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
if (info instanceof ClassInfo) {
ClassInfo classInfo = (ClassInfo) info;
try {
Class clazz = classInfo.get();
// double check via proper reflection
if (!checkRuntimeAnnotation || clazz.isAnnotationPresent(annotation)) {
classes.add(clazz);
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
}
}
}
return classes;
}
public List<Annotated<Class<?>>> findMetaAnnotatedClasses(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
Set<Class<?>> classes = findMetaAnnotatedClasses(annotation, new HashSet<Class<?>>());
List<Annotated<Class<?>>> list = new LinkedList<Annotated<Class<?>>>();
for (Class<?> clazz : classes) {
if (Annotation.class.isAssignableFrom(clazz) && isMetaAnnotation((Class<? extends Annotation>) clazz)) continue;
list.add(new MetaAnnotatedClass(clazz));
}
return list;
}
private static boolean isMetaAnnotation(Class<? extends Annotation> clazz) {
for (Annotation annotation : clazz.getDeclaredAnnotations()) {
if (isMetatypeAnnotation(annotation.annotationType())) return true;
}
return false;
}
private static boolean isMetatypeAnnotation(Class<? extends Annotation> type) {
if (isSelfAnnotated(type, "Metatype")) return true;
for (Annotation annotation : type.getAnnotations()) {
if (isSelfAnnotated(annotation.annotationType(), "Metaroot")) return true;
}
return false;
}
private static boolean isSelfAnnotated(Class<? extends Annotation> type, String name) {
return type.isAnnotationPresent(type) && type.getSimpleName().equals(name) && validTarget(type);
}
private static boolean validTarget(Class<? extends Annotation> type) {
final Target target = type.getAnnotation(Target.class);
if (target == null) return false;
final ElementType[] targets = target.value();
return targets.length == 1 && targets[0] == ElementType.ANNOTATION_TYPE;
}
private Set<Class<?>> findMetaAnnotatedClasses(Class<? extends Annotation> annotation, Set<Class<?>> classes) {
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
if (info instanceof ClassInfo) {
ClassInfo classInfo = (ClassInfo) info;
try {
Class clazz = classInfo.get();
if (classes.contains(clazz)) continue;
// double check via proper reflection
if (clazz.isAnnotationPresent(annotation)) {
classes.add(clazz);
}
String meta = info.getMetaAnnotationName();
if (meta != null) {
classes.addAll(findMetaAnnotatedClasses((Class<? extends Annotation>) clazz, classes));
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
}
}
}
return classes;
}
/**
* Naive implementation - works extremelly slow O(n^3)
*
* @param annotation
* @return list of directly or indirectly (inherited) annotated classes
*/
public List<Class<?>> findInheritedAnnotatedClasses(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
List<Class<?>> classes = new LinkedList<Class<?>>();
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
try {
if (info instanceof ClassInfo) {
classes.add(((ClassInfo) info).get());
}
} catch (ClassNotFoundException cnfe) {
// TODO: ignored, but a log message would be appropriate
}
}
boolean annClassFound;
List<ClassInfo> tempClassInfos = new ArrayList<ClassInfo>(classInfos.values());
do {
annClassFound = false;
for (int pos = 0; pos < tempClassInfos.size(); pos++) {
ClassInfo classInfo = tempClassInfos.get(pos);
try {
// check whether any superclass is annotated
String superType = classInfo.getSuperType();
for (Class clazz : classes) {
if (superType.equals(clazz.getName())) {
classes.add(classInfo.get());
tempClassInfos.remove(pos);
annClassFound = true;
break;
}
}
// check whether any interface is annotated
List<String> interfces = classInfo.getInterfaces();
for (String interfce : interfces) {
for (Class clazz : classes) {
if (interfce.replaceFirst("<.*>", "").equals(clazz.getName())) {
classes.add(classInfo.get());
tempClassInfos.remove(pos);
annClassFound = true;
break;
}
}
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
} catch (NoClassDefFoundError e) {
classesNotLoaded.add(classInfo.getName());
}
}
} while (annClassFound);
return classes;
}
public List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
List<ClassInfo> seen = new LinkedList<ClassInfo>();
List<Method> methods = new LinkedList<Method>();
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
if (info instanceof MethodInfo && !info.getName().equals("<init>")) {
final MethodInfo methodInfo = (MethodInfo) info;
if (checkRuntimeAnnotation) {
final ClassInfo classInfo = methodInfo.getDeclaringClass();
if (seen.contains(classInfo)) continue;
seen.add(classInfo);
try {
Class clazz = classInfo.get();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(annotation)) {
methods.add(method);
}
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
} catch (ClassCircularityError cce) {
classesNotLoaded.add(classInfo.getName());
}
} else {
try {
final Method method = (Method) methodInfo.get();
methods.add(method);
} catch (ClassNotFoundException e) {
classesNotLoaded.add(methodInfo.getDeclaringClass().getName());
}
}
}
}
return methods;
}
public List<Parameter<Method>> findAnnotatedMethodParameters(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
final Set<ClassInfo> seen = checkRuntimeAnnotation ? new HashSet<ClassInfo>() : null;
final List<Parameter<Method>> result = new LinkedList<Parameter<Method>>();
for (Info info : getAnnotationInfos(annotation.getName())) {
if (!(info instanceof ParameterInfo)) {
continue;
}
final ParameterInfo parameterInfo = (ParameterInfo) info;
if ("<init>".equals(parameterInfo.getDeclaringMethod().getName())) {
continue;
}
final ClassInfo classInfo = parameterInfo.getDeclaringMethod().getDeclaringClass();
if (checkRuntimeAnnotation) {
if (!seen.add(classInfo)) {
continue;
}
try {
Class<?> clazz = classInfo.get();
for (Method method : clazz.getDeclaredMethods()) {
for (Annotation[] annotations : method.getParameterAnnotations()) {
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].annotationType().equals(annotation)) {
result.add(Parameter.declaredBy(method, i));
}
}
}
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
}
} else {
try {
@SuppressWarnings("unchecked")
final Parameter<Method> parameter = (Parameter<Method>) parameterInfo.get();
result.add(parameter);
} catch (ClassNotFoundException e) {
classesNotLoaded.add(parameterInfo.getDeclaringMethod().getDeclaringClass().getName());
}
}
}
return result;
}
public List<Annotated<Method>> findMetaAnnotatedMethods(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
Set<Method> methods = findMetaAnnotatedMethods(annotation, new HashSet<Method>(), new HashSet<String>());
List<Annotated<Method>> targets = new LinkedList<Annotated<Method>>();
for (Method method : methods) {
targets.add(new MetaAnnotatedMethod(method));
}
return targets;
}
private Set<Method> findMetaAnnotatedMethods(Class<? extends Annotation> annotation, Set<Method> methods, Set<String> seen) {
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
String meta = info.getMetaAnnotationName();
if (meta != null) {
if (meta.equals(annotation.getName())) continue;
if (!seen.add(meta)) continue;
ClassInfo metaInfo = classInfos.get(meta);
Class<?> clazz;
try {
clazz = metaInfo.get();
} catch (ClassNotFoundException e) {
classesNotLoaded.add(metaInfo.getName());
continue;
}
findMetaAnnotatedMethods((Class<? extends Annotation>) clazz, methods, seen);
} else if (info instanceof MethodInfo && !((MethodInfo) info).isConstructor()) {
MethodInfo methodInfo = (MethodInfo) info;
ClassInfo classInfo = methodInfo.getDeclaringClass();
try {
Class clazz = classInfo.get();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(annotation)) {
methods.add(method);
}
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
} catch (NoClassDefFoundError ncdfe) {
classesNotLoaded.add(classInfo.getName());
}
}
}
return methods;
}
public List<Annotated<Field>> findMetaAnnotatedFields(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
Set<Field> fields = findMetaAnnotatedFields(annotation, new HashSet<Field>(), new HashSet<String>());
List<Annotated<Field>> targets = new LinkedList<Annotated<Field>>();
for (Field field : fields) {
targets.add(new MetaAnnotatedField(field));
}
return targets;
}
private Set<Field> findMetaAnnotatedFields(Class<? extends Annotation> annotation, Set<Field> fields, Set<String> seen) {
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
String meta = info.getMetaAnnotationName();
if (meta != null) {
if (meta.equals(annotation.getName())) continue;
if (!seen.add(meta)) continue;
ClassInfo metaInfo = classInfos.get(meta);
Class<?> clazz;
try {
clazz = metaInfo.get();
} catch (ClassNotFoundException e) {
classesNotLoaded.add(metaInfo.getName());
continue;
}
findMetaAnnotatedFields((Class<? extends Annotation>) clazz, fields, seen);
} else if (info instanceof FieldInfo) {
FieldInfo fieldInfo = (FieldInfo) info;
ClassInfo classInfo = fieldInfo.getDeclaringClass();
try {
Class clazz = classInfo.get();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(annotation)) {
fields.add(field);
}
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
} catch (NoClassDefFoundError ncdfe) {
classesNotLoaded.add(classInfo.getName());
}
}
}
return fields;
}
public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
List<ClassInfo> seen = new LinkedList<ClassInfo>();
List<Constructor> constructors = new LinkedList<Constructor>();
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
if (info instanceof MethodInfo && info.getName().equals("<init>")) {
MethodInfo methodInfo = (MethodInfo) info;
if (checkRuntimeAnnotation) {
ClassInfo classInfo = methodInfo.getDeclaringClass();
if (seen.contains(classInfo)) continue;
seen.add(classInfo);
try {
Class clazz = classInfo.get();
for (Constructor constructor : clazz.getConstructors()) {
if (constructor.isAnnotationPresent(annotation)) {
constructors.add(constructor);
}
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
} catch (NoClassDefFoundError ncdfe) {
classesNotLoaded.add(classInfo.getName());
}
} else {
try {
constructors.add((Constructor) methodInfo.get());
} catch (ClassNotFoundException e) {
classesNotLoaded.add(methodInfo.getDeclaringClass().getName());
}
}
}
}
return constructors;
}
public List<Parameter<Constructor<?>>> findAnnotatedConstructorParameters(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
final Set<ClassInfo> seen = checkRuntimeAnnotation ? new HashSet<ClassInfo>() : null;
final List<Parameter<Constructor<?>>> result = new LinkedList<Parameter<Constructor<?>>>();
for (Info info : getAnnotationInfos(annotation.getName())) {
if (!(info instanceof ParameterInfo)) {
continue;
}
final ParameterInfo parameterInfo = (ParameterInfo) info;
if (!"<init>".equals(parameterInfo.getDeclaringMethod().getName())) {
continue;
}
final ClassInfo classInfo = parameterInfo.getDeclaringMethod().getDeclaringClass();
if (checkRuntimeAnnotation) {
if (!seen.add(classInfo)) {
continue;
}
try {
Class<?> clazz = classInfo.get();
for (Constructor<?> ctor : clazz.getDeclaredConstructors()) {
for (Annotation[] annotations : ctor.getParameterAnnotations()) {
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].annotationType().equals(annotation)) {
@SuppressWarnings({ "rawtypes", "unchecked" })
final Parameter<Constructor<?>> parameter = Parameter.declaredBy((Constructor) ctor, i);
result.add(parameter);
}
}
}
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
}
} else {
try {
@SuppressWarnings("unchecked")
final Parameter<Constructor<?>> parameter = (Parameter<Constructor<?>>) parameterInfo.get();
result.add(parameter);
} catch (ClassNotFoundException e) {
classesNotLoaded.add(parameterInfo.getDeclaringMethod().getDeclaringClass().getName());
}
}
}
return result;
}
public List<Field> findAnnotatedFields(Class<? extends Annotation> annotation) {
classesNotLoaded.clear();
List<ClassInfo> seen = new LinkedList<ClassInfo>();
List<Field> fields = new LinkedList<Field>();
List<Info> infos = getAnnotationInfos(annotation.getName());
for (Info info : infos) {
if (info instanceof FieldInfo) {
FieldInfo fieldInfo = (FieldInfo) info;
if (checkRuntimeAnnotation) {
ClassInfo classInfo = fieldInfo.getDeclaringClass();
if (seen.contains(classInfo)) continue;
seen.add(classInfo);
try {
Class clazz = classInfo.get();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(annotation)) {
fields.add(field);
}
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
} catch (NoClassDefFoundError ncdfe) {
classesNotLoaded.add(classInfo.getName());
}
} else {
try {
fields.add((Field) fieldInfo.get());
} catch (ClassNotFoundException e) {
classesNotLoaded.add(fieldInfo.getDeclaringClass().getName());
}
}
}
}
return fields;
}
public List<Class<?>> findClassesInPackage(String packageName, boolean recursive) {
classesNotLoaded.clear();
List<Class<?>> classes = new LinkedList<Class<?>>();
for (ClassInfo classInfo : classInfos.values()) {
try {
if (recursive && classInfo.getPackageName().startsWith(packageName)) {
classes.add(classInfo.get());
} else if (classInfo.getPackageName().equals(packageName)) {
classes.add(classInfo.get());
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
}
}
return classes;
}
public <T> List<Class<? extends T>> findSubclasses(Class<T> clazz) {
if (clazz == null) throw new NullPointerException("class cannot be null");
classesNotLoaded.clear();
final ClassInfo classInfo = classInfos.get(clazz.getName());
List<Class<? extends T>> found = new LinkedList<Class<? extends T>>();
if (classInfo == null) return found;
findSubclasses(classInfo, found, clazz);
return found;
}
private <T> void findSubclasses(ClassInfo classInfo, List<Class<? extends T>> found, Class<T> clazz) {
for (ClassInfo subclassInfo : classInfo.subclassInfos) {
try {
found.add(subclassInfo.get().asSubclass(clazz));
} catch (ClassNotFoundException e) {
classesNotLoaded.add(subclassInfo.getName());
}
findSubclasses(subclassInfo, found, clazz);
}
}
private <T> List<Class<? extends T>> _findSubclasses(Class<T> clazz) {
if (clazz == null) throw new NullPointerException("class cannot be null");
List<Class<? extends T>> classes = new LinkedList<Class<? extends T>>();
for (ClassInfo classInfo : classInfos.values()) {
try {
final String name = clazz.getName();
if (name.equals(classInfo.superType)) {
if (clazz.isAssignableFrom(classInfo.get())) {
final Class<? extends T> asSubclass = classInfo.get().asSubclass(clazz);
classes.add(asSubclass);
classes.addAll(_findSubclasses(asSubclass));
}
}
} catch (ClassNotFoundException e) {
classesNotLoaded.add(classInfo.getName());
}
}
return classes;
}
public <T> List<Class<? extends T>> findImplementations(Class<T> clazz) {
if (clazz == null) throw new NullPointerException("class cannot be null");
if (!clazz.isInterface()) new IllegalArgumentException("class must be an interface");
classesNotLoaded.clear();
final String interfaceName = clazz.getName();
// Collect all interfaces extending the main interface (recursively)
// Collect all implementations of interfaces
// i.e. all *directly* implementing classes
final List<ClassInfo> infos = collectImplementations(interfaceName);
// Collect all subclasses of implementations
final List<Class<? extends T>> classes = new LinkedList<Class<? extends T>>();
for (ClassInfo info : infos) {
try {
final Class<? extends T> impl = (Class<? extends T>) info.get();
if (!classes.contains(impl) && clazz.isAssignableFrom(impl)) {
classes.add(impl);
// Optimization: Don't need to call this method if parent class was already searched
final List<Class<? extends T>> c = _findSubclasses((Class<T>) impl);
for (final Class<? extends T> cl : c) {
if (!classes.contains(cl)) {
classes.add(cl);
}
}
}
} catch (final ClassNotFoundException e) {
classesNotLoaded.add(info.getName());
}
}
return classes;
}
private List<ClassInfo> collectImplementations(String interfaceName) {
final List<ClassInfo> infos = new LinkedList<ClassInfo>();
for (ClassInfo classInfo : classInfos.values()) {
if (classInfo.interfaces.contains(interfaceName)) {
infos.add(classInfo);
try {
final Class clazz = classInfo.get();
if (clazz.isInterface() && !clazz.isAnnotation()) {
infos.addAll(collectImplementations(classInfo.name));
}
} catch (ClassNotFoundException ignore) {
// we'll deal with this later
}
}
}
return infos;
}
protected List<Info> getAnnotationInfos(String name) {
final List<Info> infos = annotated.get(name);
if (infos != null) return infos;
return Collections.EMPTY_LIST;
}
protected List<Info> initAnnotationInfos(String name) {
List<Info> infos = annotated.get(name);
if (infos == null) {
infos = new SingleLinkedList<Info>();
annotated.put(name, infos);
}
return infos;
}
protected void readClassDef(String className) {
if (classInfos.containsKey(className)) return;
try {
readClassDef(archive.getBytecode(className));
} catch (Exception e) {
if (className.endsWith("$$")) return;
classesNotLoaded.add(className);
}
}
protected void readClassDef(InputStream in) throws IOException {
try {
ClassReader classReader = new ClassReader(in);
classReader.accept(new InfoBuildingVisitor(), ASM_FLAGS);
} finally {
in.close();
}
}
protected void readClassDef(Class clazz) {
List<Info> infos = new LinkedList<Info>();
Package aPackage = clazz.getPackage();
if (aPackage != null) {
final PackageInfo info = new PackageInfo(aPackage);
for (AnnotationInfo annotation : info.getAnnotations()) {
List<Info> annotationInfos = initAnnotationInfos(annotation.getName());
if (!annotationInfos.contains(info)) {
annotationInfos.add(info);
}
}
}
ClassInfo classInfo = new ClassInfo(clazz);
infos.add(classInfo);
for (Method method : clazz.getDeclaredMethods()) {
MethodInfo methodInfo = new MethodInfo(classInfo, method);
if (linking) {
classInfo.methods.add(methodInfo);
}
infos.add(methodInfo);
for (Annotation[] annotations : method.getParameterAnnotations()) {
for (int i = 0; i < annotations.length; i++) {
infos.add(new ParameterInfo(methodInfo, i));
}
}
}
for (Constructor<?> constructor : clazz.getConstructors()) {
MethodInfo methodInfo = new MethodInfo(classInfo, constructor);
if (linking) {
classInfo.methods.add(methodInfo);
}
infos.add(methodInfo);
for (Annotation[] annotations : constructor.getParameterAnnotations()) {
for (int i = 0; i < annotations.length; i++) {
infos.add(new ParameterInfo(methodInfo, i));
}
}
}
for (Field field : clazz.getDeclaredFields()) {
final FieldInfo fieldInfo = new FieldInfo(classInfo, field);
if (linking) {
classInfo.fields.add(fieldInfo);
}
infos.add(fieldInfo);
}
for (Info info : infos) {
for (AnnotationInfo annotation : info.getAnnotations()) {
List<Info> annotationInfos = initAnnotationInfos(annotation.getName());
annotationInfos.add(info);
}
}
if (linking) {
classInfos.put(classInfo.name, classInfo);
}
}
public AnnotationFinder select(Class<?>... clazz) {
String[] names = new String[clazz.length];
int i = 0;
for (Class<?> name : clazz) {
names[i++] = name.getName();
}
return new AnnotationFinder(this, Arrays.asList(names));
}
public AnnotationFinder select(String... clazz) {
return new AnnotationFinder(this, Arrays.asList(clazz));
}
public AnnotationFinder select(Iterable<String> clazz) {
return new AnnotationFinder(this, clazz);
}
public class SubArchive implements Archive {
private List<Entry> classes = new LinkedList<Entry>();
public SubArchive(String... classes) {
for (String name : classes) {
this.classes.add(new E(name));
}
}
public SubArchive(Iterable<String> classes) {
for (String name : classes) {
this.classes.add(new E(name));
}
}
public InputStream getBytecode(String className) throws IOException, ClassNotFoundException {
return archive.getBytecode(className);
}
public Class<?> loadClass(String className) throws ClassNotFoundException {
return archive.loadClass(className);
}
public Iterator<Entry> iterator() {
return classes.iterator();
}
public class E implements Entry {
private final String name;
public E(String name) {
this.name = name;
}
public String getName() {
return name;
}
public InputStream getBytecode() throws IOException {
return new ByteArrayInputStream(new byte[0]);
}
}
}
public class Annotatable {
private final List<AnnotationInfo> annotations = new LinkedList<AnnotationInfo>();
public Annotatable(AnnotatedElement element) {
for (Annotation annotation : getAnnotations(element)) {
annotations.add(new AnnotationInfo(Type.getType(annotation.annotationType()).getDescriptor()));
}
}
public Annotatable() {
}
public Annotation[] getDeclaredAnnotations() {
return new Annotation[0];
}
public List<AnnotationInfo> getAnnotations() {
return annotations;
}
public String getMetaAnnotationName() {
return null;
}
/**
* Utility method to get around some errors caused by
* interactions between the Equinox class loaders and
* the OpenJPA transformation process. There is a window
* where the OpenJPA transformation process can cause
* an annotation being processed to get defined in a
* classloader during the actual defineClass call for
* that very class (e.g., recursively). This results in
* a LinkageError exception. If we see one of these,
* retry the request. Since the annotation will be
* defined on the second pass, this should succeed. If
* we get a second exception, then it's likely some
* other problem.
*
* @param element The AnnotatedElement we need information for.
* @return An array of the Annotations defined on the element.
*/
private Annotation[] getAnnotations(AnnotatedElement element) {
try {
return element.getAnnotations();
} catch (LinkageError e) {
return element.getAnnotations();
}
}
}
public static interface Info {
String getMetaAnnotationName();
String getName();
List<AnnotationInfo> getAnnotations();
Annotation[] getDeclaredAnnotations();
}
public class PackageInfo extends Annotatable implements Info {
private final String name;
private final ClassInfo info;
private final Package pkg;
public PackageInfo(Package pkg) {
super(pkg);
this.pkg = pkg;
this.name = pkg.getName();
this.info = null;
}
public PackageInfo(String name) {
info = new ClassInfo(name, null);
this.name = name;
this.pkg = null;
}
public String getName() {
return name;
}
public Package get() throws ClassNotFoundException {
return (pkg != null) ? pkg : info.get().getPackage();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PackageInfo that = (PackageInfo) o;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
return true;
}
@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
}
public class ClassInfo extends Annotatable implements Info {
private String name;
private final List<MethodInfo> methods = new SingleLinkedList<MethodInfo>();
private final List<MethodInfo> constructors = new SingleLinkedList<MethodInfo>();
private String superType;
private ClassInfo superclassInfo;
private final List<ClassInfo> subclassInfos = new SingleLinkedList<ClassInfo>();
private final List<String> interfaces = new SingleLinkedList<String>();
private final List<FieldInfo> fields = new SingleLinkedList<FieldInfo>();
private Class<?> clazz;
public ClassInfo(Class clazz) {
super(clazz);
this.clazz = clazz;
this.name = clazz.getName();
final Class superclass = clazz.getSuperclass();
this.superType = superclass != null ? superclass.getName() : null;
for (Class intrface : clazz.getInterfaces()) {
this.interfaces.add(intrface.getName());
}
}
public ClassInfo(final String name, final String superType) {
this.name = name;
this.superType = superType;
}
@Override
public String getMetaAnnotationName() {
for (AnnotationInfo info : getAnnotations()) {
for (Class<? extends Annotation> metaroot : metaroots) {
if (info.getName().equals(metaroot.getName())) return name;
}
}
if (name.endsWith("$$")) {
ClassInfo info = classInfos.get(name.substring(0, name.length() - 2));
if (info != null) {
return info.getMetaAnnotationName();
}
}
return null;
}
public String getPackageName() {
return name.indexOf(".") > 0 ? name.substring(0, name.lastIndexOf(".")) : "";
}
public List<MethodInfo> getConstructors() {
return constructors;
}
public List<String> getInterfaces() {
return interfaces;
}
public List<FieldInfo> getFields() {
return fields;
}
public List<MethodInfo> getMethods() {
return methods;
}
public String getName() {
return name;
}
public String getSuperType() {
return superType;
}
public boolean isAnnotation() {
return "java.lang.Object".equals(superType) && interfaces.size() == 1 && "java.lang.annotation.Annotation".equals(interfaces.get(0));
}
public Class<?> get() throws ClassNotFoundException {
if (clazz != null) return clazz;
try {
String fixedName = name.replaceFirst("<.*>", "");
this.clazz = archive.loadClass(fixedName);
return clazz;
} catch (ClassNotFoundException notFound) {
classesNotLoaded.add(name);
throw notFound;
}
}
public String toString() {
return name;
}
}
public class MethodInfo extends Annotatable implements Info {
private final ClassInfo declaringClass;
private final String descriptor;
private final String name;
private final List<List<AnnotationInfo>> parameterAnnotations = new LinkedList<List<AnnotationInfo>>();
private final List<ParameterInfo> parameters = new SingleLinkedList<ParameterInfo>();
private Member method;
public MethodInfo(ClassInfo info, Constructor constructor) {
super(constructor);
this.declaringClass = info;
this.name = "<init>";
this.descriptor = Type.getConstructorDescriptor(constructor);
}
public MethodInfo(ClassInfo info, Method method) {
super(method);
this.declaringClass = info;
this.name = method.getName();
this.descriptor = Type.getMethodDescriptor(method);
this.method = method;
}
public MethodInfo(ClassInfo declarignClass, String name, String descriptor) {
this.declaringClass = declarignClass;
this.name = name;
this.descriptor = descriptor;
}
public String getDescriptor() {
return descriptor;
}
@Override
public String getMetaAnnotationName() {
return declaringClass.getMetaAnnotationName();
}
@Override
public Annotation[] getDeclaredAnnotations() {
super.getDeclaredAnnotations();
try {
return ((AnnotatedElement) get()).getDeclaredAnnotations();
} catch (ClassNotFoundException e) {
return super.getDeclaredAnnotations();
}
}
public boolean isConstructor() {
return getName().equals("<init>");
}
public List<List<AnnotationInfo>> getParameterAnnotations() {
return parameterAnnotations;
}
public List<AnnotationInfo> getParameterAnnotations(int index) {
if (index >= parameterAnnotations.size()) {
for (int i = parameterAnnotations.size(); i <= index; i++) {
List<AnnotationInfo> annotationInfos = new LinkedList<AnnotationInfo>();
parameterAnnotations.add(i, annotationInfos);
}
}
return parameterAnnotations.get(index);
}
public List<ParameterInfo> getParameters() {
return parameters;
}
public String getName() {
return name;
}
public ClassInfo getDeclaringClass() {
return declaringClass;
}
public String toString() {
return declaringClass + "@" + name;
}
public Member get() throws ClassNotFoundException {
if (method == null) {
method = toMethod();
}
return method;
}
private Member toMethod() throws ClassNotFoundException {
org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method(name, descriptor);
Class<?> clazz = this.declaringClass.get();
List<Class> parameterTypes = new LinkedList<Class>();
for (Type type : method.getArgumentTypes()) {
String paramType = type.getClassName();
try {
parameterTypes.add(Classes.forName(paramType, clazz.getClassLoader()));
} catch (ClassNotFoundException cnfe) {
throw new IllegalStateException("Parameter class could not be loaded for type " + paramType, cnfe);
}
}
Class[] parameters = parameterTypes.toArray(new Class[parameterTypes.size()]);
IllegalStateException noSuchMethod = null;
while (clazz != null) {
try {
if (name.equals("<init>")) {
return clazz.getDeclaredConstructor(parameters);
} else {
return clazz.getDeclaredMethod(name, parameters);
}
} catch (NoSuchMethodException e) {
if (noSuchMethod == null) {
noSuchMethod = new IllegalStateException("Callback method does not exist: " + clazz.getName() + "." + name, e);
}
clazz = clazz.getSuperclass();
}
}
throw noSuchMethod;
}
}
public class ParameterInfo extends Annotatable implements Info {
private final MethodInfo declaringMethod;
private final int index;
private final List<AnnotationInfo> annotations = new LinkedList<AnnotationInfo>();
private Parameter<?> parameter;
public ParameterInfo(MethodInfo parent, int index) {
super();
this.declaringMethod = parent;
this.index = index;
}
public ParameterInfo(MethodInfo parent, Parameter<?> parameter) {
super(parameter);
this.declaringMethod = parent;
this.index = parameter.getIndex();
this.parameter = parameter;
}
public String getName() {
return Integer.toString(index);
}
public Parameter<?> get() throws ClassNotFoundException {
if (parameter == null) {
Member member = declaringMethod.get();
if (member instanceof Method) {
parameter = Parameter.declaredBy((Method) member, index);
} else if (member instanceof Constructor<?>) {
parameter = Parameter.declaredBy((Constructor<?>) member, index);
}
}
return parameter;
}
@Override
public Annotation[] getDeclaredAnnotations() {
try {
return get().getDeclaredAnnotations();
} catch (ClassNotFoundException e) {
return super.getDeclaredAnnotations();
}
}
public MethodInfo getDeclaringMethod() {
return declaringMethod;
}
@Override
public String toString() {
return String.format("%s(arg%s)", declaringMethod, index);
}
}
public class FieldInfo extends Annotatable implements Info {
private final String name;
private final String type;
private final ClassInfo declaringClass;
private Field field;
public FieldInfo(ClassInfo info, Field field) {
super(field);
this.declaringClass = info;
this.name = field.getName();
this.type = Type.getType(field.getType()).getDescriptor();
this.field = field;
}
public FieldInfo(ClassInfo declaringClass, String name, String type) {
this.declaringClass = declaringClass;
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public ClassInfo getDeclaringClass() {
return declaringClass;
}
public String getType() { // if this method starts to be used internally move this to constructors and just return type
final Type t = Type.getType(type);
if (t.getClassName() == null) {
return t.getDescriptor();
}
return t.getClassName();
}
public String toString() {
return declaringClass + "#" + name;
}
@Override
public String getMetaAnnotationName() {
return declaringClass.getMetaAnnotationName();
}
@Override
public Annotation[] getDeclaredAnnotations() {
super.getDeclaredAnnotations();
try {
return ((AnnotatedElement) get()).getDeclaredAnnotations();
} catch (ClassNotFoundException e) {
return super.getDeclaredAnnotations();
}
}
public Member get() throws ClassNotFoundException {
if (field == null) {
field = toField();
}
return field;
}
private Field toField() throws ClassNotFoundException {
Class<?> clazz = this.declaringClass.get();
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
throw new IllegalStateException(name, e);
}
}
}
public class AnnotationInfo extends Annotatable implements Info {
private final String name;
public AnnotationInfo(Annotation annotation) {
this(Type.getType(annotation.annotationType()).getDescriptor());
}
public AnnotationInfo(Class<? extends Annotation> annotation) {
this.name = annotation.getName().intern();
}
public AnnotationInfo(String name) {
final Type type = Type.getType(name);
name = type.getClassName();
if (name == null) {
name = type.getDescriptor(); // name was already a class name
}
this.name = name;
}
public String getName() {
return name;
}
public String toString() {
return name;
}
}
private void index(AnnotationInfo annotationInfo, Info info) {
initAnnotationInfos(annotationInfo.getName()).add(info);
}
public class InfoBuildingVisitor extends EmptyVisitor {
private Info info;
public InfoBuildingVisitor() {
}
public InfoBuildingVisitor(Info info) {
this.info = info;
}
public Info getInfo() {
return info;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
if (name.endsWith("package-info")) {
info = new PackageInfo(javaName(name));
} else {
ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName));
// if (signature == null) {
for (final String interfce : interfaces) {
classInfo.interfaces.add(javaName(interfce));
}
// } else {
// // the class uses generics
// new SignatureReader(signature).accept(new GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE.CLASS, classInfo));
// }
info = classInfo;
classInfos.put(classInfo.getName(), classInfo);
}
}
private String javaName(String name) {
return (name == null) ? null : name.replace('/', '.');
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public void visitAttribute(Attribute attribute) {
super.visitAttribute(attribute);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
AnnotationInfo annotationInfo = new AnnotationInfo(desc);
info.getAnnotations().add(annotationInfo);
index(annotationInfo, info);
return new InfoBuildingVisitor(annotationInfo).annotationVisitor();
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
ClassInfo classInfo = ((ClassInfo) info);
FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc);
classInfo.getFields().add(fieldInfo);
return new InfoBuildingVisitor(fieldInfo).fieldVisitor();
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
ClassInfo classInfo = ((ClassInfo) info);
MethodInfo methodInfo = new MethodInfo(classInfo, name, desc);
classInfo.getMethods().add(methodInfo);
return new InfoBuildingVisitor(methodInfo).methodVisitor();
}
@Override
public AnnotationVisitor visitMethodParameterAnnotation(int param, String desc, boolean visible) {
MethodInfo methodInfo = ((MethodInfo) info);
List<AnnotationInfo> annotationInfos = methodInfo.getParameterAnnotations(param);
AnnotationInfo annotationInfo = new AnnotationInfo(desc);
annotationInfos.add(annotationInfo);
ParameterInfo parameterInfo = new ParameterInfo(methodInfo, param);
methodInfo.getParameters().add(parameterInfo);
index(annotationInfo, parameterInfo);
return new InfoBuildingVisitor(annotationInfo).annotationVisitor();
}
}
}