001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *  http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020
021package org.apache.xbean.finder;
022
023import org.apache.xbean.asm9.original.commons.EmptyVisitor;
024import org.apache.xbean.finder.archive.Archive;
025import org.apache.xbean.finder.util.Classes;
026import org.apache.xbean.finder.util.SingleLinkedList;
027import org.objectweb.asm.AnnotationVisitor;
028import org.objectweb.asm.Attribute;
029import org.objectweb.asm.ClassReader;
030import org.objectweb.asm.FieldVisitor;
031import org.objectweb.asm.MethodVisitor;
032import org.objectweb.asm.Type;
033
034import java.io.ByteArrayInputStream;
035import java.io.IOException;
036import java.io.InputStream;
037import java.lang.annotation.Annotation;
038import java.lang.annotation.ElementType;
039import java.lang.annotation.Target;
040import java.lang.reflect.AnnotatedElement;
041import java.lang.reflect.Constructor;
042import java.lang.reflect.Field;
043import java.lang.reflect.Member;
044import java.lang.reflect.Method;
045import java.util.ArrayList;
046import java.util.Arrays;
047import java.util.Collections;
048import java.util.HashMap;
049import java.util.HashSet;
050import java.util.Iterator;
051import java.util.LinkedList;
052import java.util.List;
053import java.util.Map;
054import java.util.Set;
055
056/**
057 * ClassFinder searches the classpath of the specified classloader for
058 * packages, classes, constructors, methods, or fields with specific annotations.
059 * <p/>
060 * For security reasons ASM is used to find the annotations.  Classes are not
061 * loaded unless they match the requirements of a called findAnnotated* method.
062 * Once loaded, these classes are cached.
063 *
064 * @version $Rev: 1881759 $ $Date: 2020-09-16 10:29:43 +0200 (Wed, 16 Sep 2020) $
065 */
066public class AnnotationFinder implements IAnnotationFinder {
067    private static final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES;
068
069    // this flag is just a backdoor to allow workaround in case we impact an application, if we aresafe for 2-3 versions
070    // let remove it
071    //
072    // main issue which can happen is a parent class we dont want to scan appears,
073    // xbean.finder.prevent-lazy-linking= true will prevent it, see readClassDef(Class)
074    private static final boolean ALLOW_LAZY_LINKING = !Boolean.getBoolean("xbean.finder.prevent-lazy-linking");
075
076    private final Set<Class<? extends Annotation>> metaroots = new HashSet<Class<? extends Annotation>>();
077
078    protected final Map<String, List<Info>> annotated = newAnnotatedMap();
079
080    protected final Map<String, ClassInfo> classInfos = newClassInfoMap();
081    protected final Map<String, ClassInfo> originalInfos = newClassInfoMap();
082    private final List<String> classesNotLoaded = new LinkedList<String>();
083    private final Archive archive;
084    private final boolean checkRuntimeAnnotation;
085    private volatile boolean linking;
086
087    private AnnotationFinder(AnnotationFinder parent, Iterable<String> classNames) {
088        this.archive = new SubArchive(classNames);
089        this.checkRuntimeAnnotation = parent.checkRuntimeAnnotation;
090        this.metaroots.addAll(parent.metaroots);
091
092        for (Class<? extends Annotation> metaroot : metaroots) {
093            final ClassInfo info = parent.classInfos.get(metaroot.getName());
094            if (info == null) continue;
095            readClassDef(info);
096        }
097        for (String name : classNames) {
098            final ClassInfo info = parent.classInfos.get(name);
099            if (info == null) continue;
100            readClassDef(info);
101        }
102
103        resolveAnnotations(parent, new LinkedList<String>());
104        for (ClassInfo classInfo : classInfos.values()) {
105            if (isMetaRoot(classInfo)) {
106                try {
107                    metaroots.add((Class<? extends Annotation>) classInfo.get());
108                } catch (ClassNotFoundException e) {
109                    classesNotLoaded.add(classInfo.getName());
110                }
111            }
112        }
113
114        for (Class<? extends Annotation> metaroot : metaroots) {
115            List<Info> infoList = annotated.get(metaroot.getName());
116            for (Info info : infoList) {
117                final String className = info.getName() + "$$";
118                final ClassInfo i = parent.classInfos.get(className);
119                if (i == null) continue;
120                readClassDef(i);
121            }
122        }
123    }
124
125    protected Map<String, List<Info>> newAnnotatedMap() {
126        return new HashMap<String, List<Info>>();
127    }
128
129    protected Map<String, ClassInfo> newClassInfoMap() {
130        return new HashMap<String, ClassInfo>();
131    }
132
133    protected boolean cleanOnNaked() {
134        return false;
135    }
136
137    protected boolean isTracked(final String annotationType) {
138        return true;
139    }
140
141    /**
142     *
143     * @param archive
144     * @param checkRuntimeAnnotation Has no effect on findMetaAnnotated* methods
145     */
146    public AnnotationFinder(Archive archive, boolean checkRuntimeAnnotation) {
147        this.archive = archive;
148        this.checkRuntimeAnnotation = checkRuntimeAnnotation;
149
150        for (Archive.Entry entry : archive) {
151            final String className = entry.getName();
152            try {
153                readClassDef(entry.getName(), entry.getBytecode());
154            } catch (NoClassDefFoundError e) {
155                throw new NoClassDefFoundError("Could not fully load class: " + className + "\n due to:" + e.getMessage());
156            } catch (IOException e) {
157                e.printStackTrace();
158            }
159        }
160
161        // keep track of what was originally from the archives
162        originalInfos.putAll(classInfos);
163    }
164
165    public AnnotationFinder(Archive archive) {
166        this(archive, true);
167    }
168
169    public boolean hasMetaAnnotations() {
170        return metaroots.size() > 0;
171    }
172
173    private void readClassDef(ClassInfo info) {
174        classInfos.put(info.getName(), info);
175        index(info);
176        index(info.constructors);
177        for (MethodInfo ctor : info.constructors) {
178            index(ctor.parameters);
179        }
180        index(info.methods);
181        for (MethodInfo method : info.methods) {
182            index(method.parameters);
183        }
184        index(info.fields);
185    }
186
187    private void resolveAnnotations(AnnotationFinder parent, List<String> scanned) {
188        // Get a list of the annotations that exist before we start
189        final List<String> annotations = new ArrayList<String>(annotated.keySet());
190
191        for (String annotation : annotations) {
192            if (scanned.contains(annotation)) continue;
193            final ClassInfo info = parent.classInfos.get(annotation);
194            if (info == null) continue;
195            readClassDef(info);
196        }
197
198        // If the "annotated" list has grown, then we must scan those
199        if (annotated.keySet().size() != annotations.size()) {
200            resolveAnnotations(parent, annotations);
201        }
202    }
203
204
205    private void index(List<? extends Info> infos) {
206        for (Info i : infos) {
207            index(i);
208        }
209    }
210
211    private void index(Info i) {
212        for (AnnotationInfo annotationInfo : i.getAnnotations()) {
213            index(annotationInfo, i);
214        }
215    }
216
217    public List<String> getAnnotatedClassNames() {
218        return new ArrayList<String>(originalInfos.keySet());
219    }
220
221    public Archive getArchive() {
222        return archive;
223    }
224
225    /**
226     * The link() method must be called to successfully use the findSubclasses and findImplementations methods
227     *
228     * @return
229     * @throws java.io.IOException
230     */
231    public AnnotationFinder link() {
232
233        enableFindSubclasses();
234
235        enableFindImplementations();
236
237        enableMetaAnnotations();
238
239        return this;
240    }
241
242    public AnnotationFinder enableMetaAnnotations() {
243        // diff new and old lists
244        resolveAnnotations(new LinkedList<String>());
245
246        linkMetaAnnotations();
247
248        return this;
249    }
250
251    public AnnotationFinder enableFindImplementations() {
252        for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
253
254            linkInterfaces(classInfo);
255
256        }
257        return this;
258    }
259
260    public AnnotationFinder enableFindSubclasses() {
261        final boolean originalLinking = linking;
262        linking = ALLOW_LAZY_LINKING;
263        for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
264
265            linkParent(classInfo);
266        }
267        linking = originalLinking;
268        return this;
269    }
270
271    /**
272     * Used to support meta annotations
273     * <p/>
274     * Once the list of classes has been read from the Archive, we
275     * iterate over all the annotations that are used by those classes
276     * and recursively resolve any annotations those annotations use.
277     *
278     * @param scanned
279     * @throws ClassNotFoundException
280     * @throws IOException
281     */
282    private void resolveAnnotations(List<String> scanned) {
283        // Get a list of the annotations that exist before we start
284        final List<String> annotations = new ArrayList<String>(annotated.keySet());
285
286        for (String annotation : annotations) {
287            if (scanned.contains(annotation)) continue;
288            readClassDef(annotation);
289        }
290
291        // If the "annotated" list has grown, then we must scan those
292        if (annotated.keySet().size() != annotations.size()) {
293            resolveAnnotations(annotations);
294        }
295
296
297//        for (ClassInfo classInfo : classInfos.values()) {
298//            for (AnnotationInfo annotationInfo : classInfo.getAnnotations()) {
299//                for (AnnotationInfo info : annotationInfo.getAnnotations()) {
300//                    final String annotation = info.getName();
301//
302//                    if (hasName(annotation, "Metaroot") && !scanned.contains(annotation)) {
303//                        readClassDef(annotation);
304//                    }
305//                }
306//            }
307//        }
308    }
309
310    private void linkMetaAnnotations() {
311        for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
312            if (isMetaRoot(classInfo)) {
313                try {
314                    metaroots.add((Class<? extends Annotation>) classInfo.get());
315                } catch (ClassNotFoundException e) {
316                    classesNotLoaded.add(classInfo.getName());
317                }
318            }
319        }
320
321        for (Class<? extends Annotation> metaroot : metaroots) {
322            List<Info> infoList = annotated.get(metaroot.getName());
323            for (Info info : infoList) {
324                readClassDef(info.getName() + "$$");
325            }
326        }
327    }
328
329    private boolean isMetaRoot(ClassInfo classInfo) {
330        if (!classInfo.isAnnotation()) return false;
331
332        if (classInfo.getName().equals("javax.annotation.Metatype")) return true;
333        if (isSelfAnnotated(classInfo, "Metatype")) return true;
334        if (isSelfAnnotated(classInfo, "Metaroot")) return false;
335
336        for (AnnotationInfo annotationInfo : classInfo.getAnnotations()) {
337            final ClassInfo annotation = classInfos.get(annotationInfo.getName());
338            if (annotation == null) return false;
339            if (annotation.getName().equals("javax.annotation.Metaroot")) return true;
340            if (isSelfAnnotated(annotation, "Metaroot")) return true;
341        }
342
343        return false;
344    }
345
346    private boolean isSelfAnnotated(ClassInfo classInfo, String metatype) {
347        if (!classInfo.isAnnotation()) return false;
348
349        final String name = classInfo.getName();
350        if (!hasName(name, metatype)) return false;
351
352        for (AnnotationInfo info : classInfo.getAnnotations()) {
353            if (info.getName().equals(name)) return true;
354        }
355
356        return true;
357    }
358
359    private boolean hasName(String className, String simpleName) {
360        return className.equals(simpleName) || className.endsWith("." + simpleName) || className.endsWith("$" + simpleName);
361    }
362
363    protected void linkParent(ClassInfo classInfo) {
364        if (classInfo.superType == null) return;
365        if (isJvm(classInfo.superType)) return;
366
367        ClassInfo parentInfo = classInfo.superclassInfo;
368
369        if (parentInfo == null) {
370
371            parentInfo = classInfos.get(classInfo.superType);
372
373            if (parentInfo == null) {
374                // best scanning we can do, try it first
375                readClassDef(classInfo.superType);
376
377                parentInfo = classInfos.get(classInfo.superType);
378
379                if (parentInfo == null) {
380                    // parentInfo == null means readClassDef fails so clean up error and retry
381                    classesNotLoaded.remove(classInfo.superType);
382
383                    try {
384                        if (classInfo.get() != null) { // call get() to ensure clazz got a change to be loaded
385                            readClassDef(((Class<?>) classInfo.clazz).getSuperclass());
386                            parentInfo = classInfos.get(classInfo.superType);
387                        }
388                    } catch (final ClassNotFoundException e) {
389                        // no-op
390                    } catch (final Throwable e) {
391                        // no-op
392                    }
393                }
394
395                if (parentInfo == null) return;
396
397                linkParent(parentInfo);
398            }
399
400            classInfo.superclassInfo = parentInfo;
401        }
402
403        synchronized (parentInfo.subclassInfos) {
404            if (!parentInfo.subclassInfos.contains(classInfo)) {
405                parentInfo.subclassInfos.add(classInfo);
406            }
407        }
408    }
409
410    /*
411    protected boolean isJvm(final String superType) {
412        // TODO: can't we simply do startsWith("java")?
413        return superType.startsWith("java.lang.")
414                || superType.startsWith("java.beans.")
415                || superType.startsWith("java.util.")
416                || superType.startsWith("java.io.")
417                || superType.startsWith("java.text.")
418                || superType.startsWith("java.net.")
419                || superType.startsWith("java.sql.")
420                || superType.startsWith("java.security.")
421                || superType.startsWith("java.awt.")
422                || superType.startsWith("javax.swing.");
423    }
424    */
425
426    protected boolean isJvm(final String name) {
427        return name.startsWith("java.");
428    }
429
430    protected void linkInterfaces(ClassInfo classInfo) {
431        final List<ClassInfo> infos = new LinkedList<ClassInfo>();
432
433        if (classInfo.clazz != null) {
434            final Class<?>[] interfaces = classInfo.clazz.getInterfaces();
435
436            for (Class<?> clazz : interfaces) {
437                ClassInfo interfaceInfo = classInfos.get(clazz.getName());
438
439                if (interfaceInfo == null) {
440                    readClassDef(clazz);
441                }
442
443                interfaceInfo = classInfos.get(clazz.getName());
444
445                if (interfaceInfo != null) {
446                    infos.add(interfaceInfo);
447                }
448            }
449        } else {
450            for (final String className : classInfo.interfaces) {
451                if (isJvm(className)) {
452                    continue;
453                }
454                ClassInfo interfaceInfo = classInfos.get(className);
455
456                if (interfaceInfo == null) {
457                    readClassDef(className);
458                }
459
460                interfaceInfo = classInfos.get(className);
461
462                if (interfaceInfo != null) {
463                    infos.add(interfaceInfo);
464                }
465            }
466        }
467
468        for (ClassInfo info : infos) {
469            linkInterfaces(info);
470        }
471    }
472
473    public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
474        List<Info> infos = annotated.get(annotation.getName());
475        return infos != null && !infos.isEmpty();
476    }
477
478    /**
479     * Returns a list of classes that could not be loaded in last invoked findAnnotated* method.
480     * <p/>
481     * The list will only contain entries of classes whose byte code matched the requirements
482     * of last invoked find* method, but were unable to be loaded and included in the results.
483     * <p/>
484     * The list returned is unmodifiable.  Once obtained, the returned list will be a live view of the
485     * results from the last findAnnotated* method call.
486     * <p/>
487     * This method is not thread safe.
488     *
489     * @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call.
490     */
491    public List<String> getClassesNotLoaded() {
492        return Collections.unmodifiableList(classesNotLoaded);
493    }
494
495    public List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation) {
496        classesNotLoaded.clear();
497        List<Package> packages = new LinkedList<Package>();
498        List<Info> infos = getAnnotationInfos(annotation.getName());
499        for (Info info : infos) {
500            if (info instanceof PackageInfo) {
501                PackageInfo packageInfo = (PackageInfo) info;
502                try {
503                    Package pkg = packageInfo.get();
504                    // double check via proper reflection
505                    if (!checkRuntimeAnnotation || pkg.isAnnotationPresent(annotation)) {
506                        packages.add(pkg);
507                    }
508                } catch (ClassNotFoundException e) {
509                    classesNotLoaded.add(packageInfo.getName());
510                }
511            }
512        }
513        return packages;
514    }
515
516    public List<Class<?>> findAnnotatedClasses(Class<? extends Annotation> annotation) {
517        classesNotLoaded.clear();
518        List<Class<?>> classes = new LinkedList<Class<?>>();
519        List<Info> infos = getAnnotationInfos(annotation.getName());
520        for (Info info : infos) {
521            if (info instanceof ClassInfo) {
522                ClassInfo classInfo = (ClassInfo) info;
523                try {
524                    Class clazz = classInfo.get();
525                    // double check via proper reflection
526                    if (!checkRuntimeAnnotation || clazz.isAnnotationPresent(annotation)) {
527                        classes.add(clazz);
528                    }
529                } catch (ClassNotFoundException e) {
530                    classesNotLoaded.add(classInfo.getName());
531                }
532            }
533        }
534        return classes;
535    }
536
537    public List<Annotated<Class<?>>> findMetaAnnotatedClasses(Class<? extends Annotation> annotation) {
538        classesNotLoaded.clear();
539        Set<Class<?>> classes = findMetaAnnotatedClasses(annotation, new HashSet<Class<?>>());
540
541        List<Annotated<Class<?>>> list = new LinkedList<Annotated<Class<?>>>();
542
543        for (Class<?> clazz : classes) {
544            if (Annotation.class.isAssignableFrom(clazz) && isMetaAnnotation((Class<? extends Annotation>) clazz)) continue;
545            list.add(new MetaAnnotatedClass(clazz));
546        }
547
548        return list;
549    }
550
551    private static boolean isMetaAnnotation(Class<? extends Annotation> clazz) {
552        for (Annotation annotation : clazz.getDeclaredAnnotations()) {
553            if (isMetatypeAnnotation(annotation.annotationType())) return true;
554        }
555
556        return false;
557    }
558
559    private static boolean isMetatypeAnnotation(Class<? extends Annotation> type) {
560        if (isSelfAnnotated(type, "Metatype")) return true;
561
562        for (Annotation annotation : type.getAnnotations()) {
563            if (isSelfAnnotated(annotation.annotationType(), "Metaroot")) return true;
564        }
565
566        return false;
567    }
568
569    private static boolean isSelfAnnotated(Class<? extends Annotation> type, String name) {
570        return type.isAnnotationPresent(type) && type.getSimpleName().equals(name) && validTarget(type);
571    }
572
573    private static boolean validTarget(Class<? extends Annotation> type) {
574        final Target target = type.getAnnotation(Target.class);
575
576        if (target == null) return false;
577
578        final ElementType[] targets = target.value();
579
580        return targets.length == 1 && targets[0] == ElementType.ANNOTATION_TYPE;
581    }
582
583
584    private Set<Class<?>> findMetaAnnotatedClasses(Class<? extends Annotation> annotation, Set<Class<?>> classes) {
585        List<Info> infos = getAnnotationInfos(annotation.getName());
586        for (Info info : infos) {
587            if (info instanceof ClassInfo) {
588                ClassInfo classInfo = (ClassInfo) info;
589                try {
590                    Class clazz = classInfo.get();
591
592                    if (classes.contains(clazz)) continue;
593
594                    // double check via proper reflection
595                    if (clazz.isAnnotationPresent(annotation)) {
596                        classes.add(clazz);
597                    }
598
599                    String meta = info.getMetaAnnotationName();
600                    if (meta != null) {
601                        classes.addAll(findMetaAnnotatedClasses((Class<? extends Annotation>) clazz, classes));
602                    }
603                } catch (ClassNotFoundException e) {
604                    classesNotLoaded.add(classInfo.getName());
605                }
606            }
607        }
608        return classes;
609    }
610
611    /**
612     * Naive implementation - works extremelly slow O(n^3)
613     *
614     * @param annotation
615     * @return list of directly or indirectly (inherited) annotated classes
616     */
617    public List<Class<?>> findInheritedAnnotatedClasses(Class<? extends Annotation> annotation) {
618        classesNotLoaded.clear();
619        List<Class<?>> classes = new LinkedList<Class<?>>();
620        List<Info> infos = getAnnotationInfos(annotation.getName());
621        for (Info info : infos) {
622            try {
623                if (info instanceof ClassInfo) {
624                    classes.add(((ClassInfo) info).get());
625                }
626            } catch (ClassNotFoundException cnfe) {
627                // TODO: ignored, but a log message would be appropriate
628            }
629        }
630        boolean annClassFound;
631        List<ClassInfo> tempClassInfos = new ArrayList<ClassInfo>(classInfos.values());
632        do {
633            annClassFound = false;
634            for (int pos = 0; pos < tempClassInfos.size(); pos++) {
635                ClassInfo classInfo = tempClassInfos.get(pos);
636                try {
637                    // check whether any superclass is annotated
638                    String superType = classInfo.getSuperType();
639                    for (Class clazz : classes) {
640                        if (superType.equals(clazz.getName())) {
641                            classes.add(classInfo.get());
642                            tempClassInfos.remove(pos);
643                            annClassFound = true;
644                            break;
645                        }
646                    }
647                    // check whether any interface is annotated
648                    List<String> interfces = classInfo.getInterfaces();
649                    for (String interfce : interfces) {
650                        for (Class clazz : classes) {
651                            if (interfce.replaceFirst("<.*>", "").equals(clazz.getName())) {
652                                classes.add(classInfo.get());
653                                tempClassInfos.remove(pos);
654                                annClassFound = true;
655                                break;
656                            }
657                        }
658                    }
659                } catch (ClassNotFoundException e) {
660                    classesNotLoaded.add(classInfo.getName());
661                } catch (NoClassDefFoundError e) {
662                    classesNotLoaded.add(classInfo.getName());
663                }
664            }
665        } while (annClassFound);
666        return classes;
667    }
668
669    public List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation) {
670        classesNotLoaded.clear();
671        List<ClassInfo> seen = new LinkedList<ClassInfo>();
672        List<Method> methods = new LinkedList<Method>();
673        List<Info> infos = getAnnotationInfos(annotation.getName());
674        for (Info info : infos) {
675            if (info instanceof MethodInfo && !info.getName().equals("<init>")) {
676                final MethodInfo methodInfo = (MethodInfo) info;
677
678                if (checkRuntimeAnnotation) {
679                    final ClassInfo classInfo = methodInfo.getDeclaringClass();
680
681                    if (seen.contains(classInfo)) continue;
682
683                    seen.add(classInfo);
684
685                    try {
686                        Class clazz = classInfo.get();
687                        for (Method method : clazz.getDeclaredMethods()) {
688                            if (method.isAnnotationPresent(annotation)) {
689                                methods.add(method);
690                            }
691                        }
692                    } catch (ClassNotFoundException e) {
693                        classesNotLoaded.add(classInfo.getName());
694                    } catch (ClassCircularityError cce) {
695                        classesNotLoaded.add(classInfo.getName());
696                    }
697                } else {
698                    try {
699                        final Method method = (Method) methodInfo.get();
700                        methods.add(method);
701                    } catch (ClassNotFoundException e) {
702                        classesNotLoaded.add(methodInfo.getDeclaringClass().getName());
703                    }
704                }
705            }
706        }
707        return methods;
708    }
709
710    public List<Parameter<Method>> findAnnotatedMethodParameters(Class<? extends Annotation> annotation) {
711        classesNotLoaded.clear();
712
713        final Set<ClassInfo> seen = checkRuntimeAnnotation ? new HashSet<ClassInfo>() : null;
714        final List<Parameter<Method>> result = new LinkedList<Parameter<Method>>();
715        for (Info info : getAnnotationInfos(annotation.getName())) {
716            if (!(info instanceof ParameterInfo)) {
717                continue;
718            }
719            final ParameterInfo parameterInfo = (ParameterInfo) info;
720            if ("<init>".equals(parameterInfo.getDeclaringMethod().getName())) {
721                continue;
722            }
723            final ClassInfo classInfo = parameterInfo.getDeclaringMethod().getDeclaringClass();
724
725            if (checkRuntimeAnnotation) {
726                if (!seen.add(classInfo)) {
727                    continue;
728                }
729                try {
730                    Class<?> clazz = classInfo.get();
731                    for (Method method : clazz.getDeclaredMethods()) {
732                        for (Annotation[] annotations : method.getParameterAnnotations()) {
733                            for (int i = 0; i < annotations.length; i++) {
734                                if (annotations[i].annotationType().equals(annotation)) {
735                                    result.add(Parameter.declaredBy(method, i));
736                                }
737                            }
738                        }
739                    }
740                } catch (ClassNotFoundException e) {
741                    classesNotLoaded.add(classInfo.getName());
742                }
743            } else {
744                try {
745                    @SuppressWarnings("unchecked")
746                    final Parameter<Method> parameter = (Parameter<Method>) parameterInfo.get();
747                    result.add(parameter);
748                } catch (ClassNotFoundException e) {
749                    classesNotLoaded.add(parameterInfo.getDeclaringMethod().getDeclaringClass().getName());
750                }
751            }
752        }
753        return result;
754    }
755
756    public List<Annotated<Method>> findMetaAnnotatedMethods(Class<? extends Annotation> annotation) {
757        classesNotLoaded.clear();
758
759        Set<Method> methods = findMetaAnnotatedMethods(annotation, new HashSet<Method>(), new HashSet<String>());
760
761        List<Annotated<Method>> targets = new LinkedList<Annotated<Method>>();
762
763        for (Method method : methods) {
764            targets.add(new MetaAnnotatedMethod(method));
765        }
766
767        return targets;
768    }
769
770    private Set<Method> findMetaAnnotatedMethods(Class<? extends Annotation> annotation, Set<Method> methods, Set<String> seen) {
771        List<Info> infos = getAnnotationInfos(annotation.getName());
772
773        for (Info info : infos) {
774
775            String meta = info.getMetaAnnotationName();
776            if (meta != null) {
777                if (meta.equals(annotation.getName())) continue;
778                if (!seen.add(meta)) continue;
779
780
781                ClassInfo metaInfo = classInfos.get(meta);
782
783                Class<?> clazz;
784                try {
785                    clazz = metaInfo.get();
786                } catch (ClassNotFoundException e) {
787                    classesNotLoaded.add(metaInfo.getName());
788                    continue;
789                }
790
791                findMetaAnnotatedMethods((Class<? extends Annotation>) clazz, methods, seen);
792
793            } else if (info instanceof MethodInfo && !((MethodInfo) info).isConstructor()) {
794
795                MethodInfo methodInfo = (MethodInfo) info;
796
797                ClassInfo classInfo = methodInfo.getDeclaringClass();
798
799                try {
800                    Class clazz = classInfo.get();
801                    for (Method method : clazz.getDeclaredMethods()) {
802                        if (method.isAnnotationPresent(annotation)) {
803                            methods.add(method);
804                        }
805                    }
806                } catch (ClassNotFoundException e) {
807                    classesNotLoaded.add(classInfo.getName());
808                } catch (NoClassDefFoundError ncdfe) {
809                    classesNotLoaded.add(classInfo.getName());
810                }
811            }
812        }
813
814        return methods;
815    }
816
817    public List<Annotated<Field>> findMetaAnnotatedFields(Class<? extends Annotation> annotation) {
818        classesNotLoaded.clear();
819
820        Set<Field> fields = findMetaAnnotatedFields(annotation, new HashSet<Field>(), new HashSet<String>());
821
822        List<Annotated<Field>> targets = new LinkedList<Annotated<Field>>();
823
824        for (Field field : fields) {
825            targets.add(new MetaAnnotatedField(field));
826        }
827
828        return targets;
829    }
830
831    private Set<Field> findMetaAnnotatedFields(Class<? extends Annotation> annotation, Set<Field> fields, Set<String> seen) {
832        List<Info> infos = getAnnotationInfos(annotation.getName());
833
834        for (Info info : infos) {
835
836            String meta = info.getMetaAnnotationName();
837            if (meta != null) {
838                if (meta.equals(annotation.getName())) continue;
839                if (!seen.add(meta)) continue;
840
841
842                ClassInfo metaInfo = classInfos.get(meta);
843
844                Class<?> clazz;
845                try {
846                    clazz = metaInfo.get();
847                } catch (ClassNotFoundException e) {
848                    classesNotLoaded.add(metaInfo.getName());
849                    continue;
850                }
851
852                findMetaAnnotatedFields((Class<? extends Annotation>) clazz, fields, seen);
853
854            } else if (info instanceof FieldInfo) {
855
856                FieldInfo fieldInfo = (FieldInfo) info;
857
858                ClassInfo classInfo = fieldInfo.getDeclaringClass();
859
860                try {
861                    Class clazz = classInfo.get();
862                    for (Field field : clazz.getDeclaredFields()) {
863                        if (field.isAnnotationPresent(annotation)) {
864                            fields.add(field);
865                        }
866                    }
867                } catch (ClassNotFoundException e) {
868                    classesNotLoaded.add(classInfo.getName());
869                } catch (NoClassDefFoundError ncdfe) {
870                    classesNotLoaded.add(classInfo.getName());
871                }
872            }
873        }
874
875        return fields;
876    }
877
878    public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
879        classesNotLoaded.clear();
880        List<ClassInfo> seen = new LinkedList<ClassInfo>();
881        List<Constructor> constructors = new LinkedList<Constructor>();
882        List<Info> infos = getAnnotationInfos(annotation.getName());
883        for (Info info : infos) {
884            if (info instanceof MethodInfo && info.getName().equals("<init>")) {
885                MethodInfo methodInfo = (MethodInfo) info;
886
887                if (checkRuntimeAnnotation) {
888                    ClassInfo classInfo = methodInfo.getDeclaringClass();
889
890                    if (seen.contains(classInfo)) continue;
891
892                    seen.add(classInfo);
893
894                    try {
895                        Class clazz = classInfo.get();
896                        for (Constructor constructor : clazz.getConstructors()) {
897                            if (constructor.isAnnotationPresent(annotation)) {
898                                constructors.add(constructor);
899                            }
900                        }
901                    } catch (ClassNotFoundException e) {
902                        classesNotLoaded.add(classInfo.getName());
903                    } catch (NoClassDefFoundError ncdfe) {
904                        classesNotLoaded.add(classInfo.getName());
905                    }
906                } else {
907                    try {
908                        constructors.add((Constructor) methodInfo.get());
909                    } catch (ClassNotFoundException e) {
910                        classesNotLoaded.add(methodInfo.getDeclaringClass().getName());
911                    }
912                }
913            }
914        }
915        return constructors;
916    }
917
918    public List<Parameter<Constructor<?>>> findAnnotatedConstructorParameters(Class<? extends Annotation> annotation) {
919        classesNotLoaded.clear();
920
921        final Set<ClassInfo> seen = checkRuntimeAnnotation ? new HashSet<ClassInfo>() : null;
922        final List<Parameter<Constructor<?>>> result = new LinkedList<Parameter<Constructor<?>>>();
923        for (Info info : getAnnotationInfos(annotation.getName())) {
924            if (!(info instanceof ParameterInfo)) {
925                continue;
926            }
927            final ParameterInfo parameterInfo = (ParameterInfo) info;
928            if (!"<init>".equals(parameterInfo.getDeclaringMethod().getName())) {
929                continue;
930            }
931            final ClassInfo classInfo = parameterInfo.getDeclaringMethod().getDeclaringClass();
932
933            if (checkRuntimeAnnotation) {
934                if (!seen.add(classInfo)) {
935                    continue;
936                }
937                try {
938                    Class<?> clazz = classInfo.get();
939                    for (Constructor<?> ctor : clazz.getDeclaredConstructors()) {
940                        for (Annotation[] annotations : ctor.getParameterAnnotations()) {
941                            for (int i = 0; i < annotations.length; i++) {
942                                if (annotations[i].annotationType().equals(annotation)) {
943                                    @SuppressWarnings({ "rawtypes", "unchecked" })
944                                    final Parameter<Constructor<?>> parameter = Parameter.declaredBy((Constructor) ctor, i);
945                                    result.add(parameter);
946                                }
947                            }
948                        }
949                    }
950                } catch (ClassNotFoundException e) {
951                    classesNotLoaded.add(classInfo.getName());
952                }
953            } else {
954                try {
955                    @SuppressWarnings("unchecked")
956                    final Parameter<Constructor<?>> parameter = (Parameter<Constructor<?>>) parameterInfo.get();
957                    result.add(parameter);
958                } catch (ClassNotFoundException e) {
959                    classesNotLoaded.add(parameterInfo.getDeclaringMethod().getDeclaringClass().getName());
960                }
961            }
962        }
963        return result;
964    }
965
966    public List<Field> findAnnotatedFields(Class<? extends Annotation> annotation) {
967        classesNotLoaded.clear();
968        List<ClassInfo> seen = new LinkedList<ClassInfo>();
969        List<Field> fields = new LinkedList<Field>();
970        List<Info> infos = getAnnotationInfos(annotation.getName());
971        for (Info info : infos) {
972            if (info instanceof FieldInfo) {
973                FieldInfo fieldInfo = (FieldInfo) info;
974
975                if (checkRuntimeAnnotation) {
976                    ClassInfo classInfo = fieldInfo.getDeclaringClass();
977
978                    if (seen.contains(classInfo)) continue;
979
980                    seen.add(classInfo);
981
982                    try {
983                        Class clazz = classInfo.get();
984                        for (Field field : clazz.getDeclaredFields()) {
985                            if (field.isAnnotationPresent(annotation)) {
986                                fields.add(field);
987                            }
988                        }
989                    } catch (ClassNotFoundException e) {
990                        classesNotLoaded.add(classInfo.getName());
991                    } catch (NoClassDefFoundError ncdfe) {
992                        classesNotLoaded.add(classInfo.getName());
993                    }
994                } else {
995                    try {
996                        fields.add((Field) fieldInfo.get());
997                    } catch (ClassNotFoundException e) {
998                        classesNotLoaded.add(fieldInfo.getDeclaringClass().getName());
999                    }
1000                }
1001            }
1002        }
1003        return fields;
1004    }
1005
1006    public List<Class<?>> findClassesInPackage(String packageName, boolean recursive) {
1007        classesNotLoaded.clear();
1008        List<Class<?>> classes = new LinkedList<Class<?>>();
1009        for (ClassInfo classInfo : classInfos.values()) {
1010            try {
1011                if (recursive && classInfo.getPackageName().startsWith(packageName)) {
1012                    classes.add(classInfo.get());
1013                } else if (classInfo.getPackageName().equals(packageName)) {
1014                    classes.add(classInfo.get());
1015                }
1016            } catch (ClassNotFoundException e) {
1017                classesNotLoaded.add(classInfo.getName());
1018            }
1019        }
1020        return classes;
1021    }
1022
1023    public <T> List<Class<? extends T>> findSubclasses(Class<T> clazz) {
1024        if (clazz == null) throw new NullPointerException("class cannot be null");
1025
1026        classesNotLoaded.clear();
1027
1028        final ClassInfo classInfo = classInfos.get(clazz.getName());
1029
1030        List<Class<? extends T>> found = new LinkedList<Class<? extends T>>();
1031
1032        if (classInfo == null) return found;
1033
1034        findSubclasses(classInfo, found, clazz);
1035
1036        return found;
1037    }
1038
1039    private <T> void findSubclasses(ClassInfo classInfo, List<Class<? extends T>> found, Class<T> clazz) {
1040
1041        for (ClassInfo subclassInfo : classInfo.subclassInfos) {
1042
1043            try {
1044                found.add(subclassInfo.get().asSubclass(clazz));
1045            } catch (ClassNotFoundException e) {
1046                classesNotLoaded.add(subclassInfo.getName());
1047            }
1048
1049            findSubclasses(subclassInfo, found, clazz);
1050        }
1051    }
1052
1053    private <T> List<Class<? extends T>> _findSubclasses(Class<T> clazz) {
1054        if (clazz == null) throw new NullPointerException("class cannot be null");
1055
1056        List<Class<? extends T>> classes = new LinkedList<Class<? extends T>>();
1057
1058
1059        for (ClassInfo classInfo : classInfos.values()) {
1060
1061            try {
1062
1063                final String name = clazz.getName();
1064                if (name.equals(classInfo.superType)) {
1065
1066                    if (clazz.isAssignableFrom(classInfo.get())) {
1067                        final Class<? extends T> asSubclass = classInfo.get().asSubclass(clazz);
1068                        classes.add(asSubclass);
1069                        classes.addAll(_findSubclasses(asSubclass));
1070                    }
1071                }
1072
1073            } catch (ClassNotFoundException e) {
1074                classesNotLoaded.add(classInfo.getName());
1075            }
1076
1077        }
1078
1079        return classes;
1080    }
1081
1082    public <T> List<Class<? extends T>> findImplementations(Class<T> clazz) {
1083        if (clazz == null) throw new NullPointerException("class cannot be null");
1084        if (!clazz.isInterface()) new IllegalArgumentException("class must be an interface");
1085        classesNotLoaded.clear();
1086
1087        final String interfaceName = clazz.getName();
1088
1089        // Collect all interfaces extending the main interface (recursively)
1090        // Collect all implementations of interfaces
1091        // i.e. all *directly* implementing classes
1092        final List<ClassInfo> infos = collectImplementations(interfaceName);
1093
1094        // Collect all subclasses of implementations
1095        final List<Class<? extends T>> classes = new LinkedList<Class<? extends T>>();
1096        for (ClassInfo info : infos) {
1097            try {
1098                final Class<? extends T> impl = (Class<? extends T>) info.get();
1099
1100                if (!classes.contains(impl) && clazz.isAssignableFrom(impl)) {
1101                    classes.add(impl);
1102
1103                    // Optimization: Don't need to call this method if parent class was already searched
1104
1105
1106                    final List<Class<? extends T>> c = _findSubclasses((Class<T>) impl);
1107                    for (final Class<? extends T> cl : c) {
1108                        if (!classes.contains(cl)) {
1109                            classes.add(cl);
1110                        }
1111                    }
1112                }
1113
1114            } catch (final ClassNotFoundException e) {
1115                classesNotLoaded.add(info.getName());
1116            }
1117        }
1118        return classes;
1119    }
1120
1121    private List<ClassInfo> collectImplementations(String interfaceName) {
1122        final List<ClassInfo> infos = new LinkedList<ClassInfo>();
1123
1124        for (ClassInfo classInfo : classInfos.values()) {
1125
1126            if (classInfo.interfaces.contains(interfaceName)) {
1127
1128                infos.add(classInfo);
1129
1130                try {
1131
1132                    final Class clazz = classInfo.get();
1133
1134                    if (clazz.isInterface() && !clazz.isAnnotation()) {
1135
1136                        infos.addAll(collectImplementations(classInfo.name));
1137
1138                    }
1139
1140                } catch (ClassNotFoundException ignore) {
1141                    // we'll deal with this later
1142                }
1143            }
1144        }
1145        return infos;
1146    }
1147
1148    protected List<Info> getAnnotationInfos(String name) {
1149        final List<Info> infos = annotated.get(name);
1150        if (infos != null) return infos;
1151        return Collections.EMPTY_LIST;
1152    }
1153
1154    protected List<Info> initAnnotationInfos(String name) {
1155        List<Info> infos = annotated.get(name);
1156        if (infos == null) {
1157            infos = new SingleLinkedList<Info>();
1158            annotated.put(name, infos);
1159        }
1160        return infos;
1161    }
1162
1163    protected void readClassDef(final String className) {
1164        if (classInfos.containsKey(className)) return;
1165        try {
1166            readClassDef(className, archive.getBytecode(className));
1167
1168        } catch (Exception e) {
1169            if (className.endsWith("$$")) return;
1170            classesNotLoaded.add(className);
1171        }
1172    }
1173
1174    protected void readClassDef(final String className, InputStream in) throws IOException {
1175        try {
1176            ClassReader classReader = new ClassReader(in);
1177            classReader.accept(new InfoBuildingVisitor(), ASM_FLAGS);
1178
1179        } catch (final Exception e) {
1180            throw new RuntimeException("Unable to read class definition for " + className, e);
1181
1182        } finally {
1183            in.close();
1184        }
1185    }
1186
1187    protected void readClassDef(Class clazz) {
1188        List<Info> infos = new LinkedList<Info>();
1189
1190        Package aPackage = clazz.getPackage();
1191        if (aPackage != null) {
1192            final PackageInfo info = new PackageInfo(aPackage);
1193            for (AnnotationInfo annotation : info.getAnnotations()) {
1194                List<Info> annotationInfos = initAnnotationInfos(annotation.getName());
1195                if (!annotationInfos.contains(info)) {
1196                    annotationInfos.add(info);
1197                }
1198            }
1199        }
1200
1201        ClassInfo classInfo = new ClassInfo(clazz);
1202        infos.add(classInfo);
1203        for (Method method : clazz.getDeclaredMethods()) {
1204            MethodInfo methodInfo = new MethodInfo(classInfo, method);
1205            if (linking) {
1206                classInfo.methods.add(methodInfo);
1207            }
1208            infos.add(methodInfo);
1209            for (Annotation[] annotations : method.getParameterAnnotations()) {
1210                for (int i = 0; i < annotations.length; i++) {
1211                    infos.add(new ParameterInfo(methodInfo, i));
1212                }
1213            }
1214        }
1215
1216        for (Constructor<?> constructor : clazz.getConstructors()) {
1217            MethodInfo methodInfo = new MethodInfo(classInfo, constructor);
1218            if (linking) {
1219                classInfo.methods.add(methodInfo);
1220            }
1221            infos.add(methodInfo);
1222            for (Annotation[] annotations : constructor.getParameterAnnotations()) {
1223                for (int i = 0; i < annotations.length; i++) {
1224                    infos.add(new ParameterInfo(methodInfo, i));
1225                }
1226            }
1227        }
1228
1229        for (Field field : clazz.getDeclaredFields()) {
1230            final FieldInfo fieldInfo = new FieldInfo(classInfo, field);
1231            if (linking) {
1232                classInfo.fields.add(fieldInfo);
1233            }
1234            infos.add(fieldInfo);
1235        }
1236
1237        for (Info info : infos) {
1238            for (AnnotationInfo annotation : info.getAnnotations()) {
1239                List<Info> annotationInfos = initAnnotationInfos(annotation.getName());
1240                annotationInfos.add(info);
1241            }
1242        }
1243
1244        if (linking) {
1245            classInfos.put(classInfo.name, classInfo);
1246        }
1247    }
1248
1249    public AnnotationFinder select(Class<?>... clazz) {
1250        String[] names = new String[clazz.length];
1251        int i = 0;
1252        for (Class<?> name : clazz) {
1253            names[i++] = name.getName();
1254        }
1255
1256        return new AnnotationFinder(this, Arrays.asList(names));
1257    }
1258
1259    public AnnotationFinder select(String... clazz) {
1260        return new AnnotationFinder(this, Arrays.asList(clazz));
1261    }
1262
1263    public AnnotationFinder select(Iterable<String> clazz) {
1264        return new AnnotationFinder(this, clazz);
1265    }
1266
1267    public class SubArchive implements Archive {
1268        private List<Entry> classes = new LinkedList<Entry>();
1269
1270        public SubArchive(String... classes) {
1271            for (String name : classes) {
1272                this.classes.add(new E(name));
1273            }
1274        }
1275
1276        public SubArchive(Iterable<String> classes) {
1277            for (String name : classes) {
1278                this.classes.add(new E(name));
1279            }
1280        }
1281
1282        public InputStream getBytecode(String className) throws IOException, ClassNotFoundException {
1283            return archive.getBytecode(className);
1284        }
1285
1286        public Class<?> loadClass(String className) throws ClassNotFoundException {
1287            return archive.loadClass(className);
1288        }
1289
1290        public Iterator<Entry> iterator() {
1291            return classes.iterator();
1292        }
1293
1294        public class E implements Entry {
1295            private final String name;
1296
1297            public E(String name) {
1298                this.name = name;
1299            }
1300
1301            public String getName() {
1302                return name;
1303            }
1304
1305            public InputStream getBytecode() throws IOException {
1306                return new ByteArrayInputStream(new byte[0]);
1307            }
1308        }
1309    }
1310
1311    public class Annotatable {
1312        private final List<AnnotationInfo> annotations = new LinkedList<AnnotationInfo>();
1313
1314        public Annotatable(AnnotatedElement element) {
1315            for (Annotation annotation : getAnnotations(element)) {
1316                annotations.add(new AnnotationInfo(Type.getType(annotation.annotationType()).getDescriptor()));
1317            }
1318        }
1319
1320        public Annotatable() {
1321        }
1322
1323        public Annotation[] getDeclaredAnnotations() {
1324            return new Annotation[0];
1325        }
1326
1327        public List<AnnotationInfo> getAnnotations() {
1328            return annotations;
1329        }
1330
1331        public String getMetaAnnotationName() {
1332            return null;
1333        }
1334
1335        /**
1336         * Utility method to get around some errors caused by
1337         * interactions between the Equinox class loaders and
1338         * the OpenJPA transformation process.  There is a window
1339         * where the OpenJPA transformation process can cause
1340         * an annotation being processed to get defined in a
1341         * classloader during the actual defineClass call for
1342         * that very class (e.g., recursively).  This results in
1343         * a LinkageError exception.  If we see one of these,
1344         * retry the request.  Since the annotation will be
1345         * defined on the second pass, this should succeed.  If
1346         * we get a second exception, then it's likely some
1347         * other problem.
1348         *
1349         * @param element The AnnotatedElement we need information for.
1350         * @return An array of the Annotations defined on the element.
1351         */
1352        private Annotation[] getAnnotations(AnnotatedElement element) {
1353            try {
1354                return element.getAnnotations();
1355            } catch (LinkageError e) {
1356                return element.getAnnotations();
1357            }
1358        }
1359
1360    }
1361
1362    public static interface Info {
1363
1364        String getMetaAnnotationName();
1365
1366        String getName();
1367
1368        List<AnnotationInfo> getAnnotations();
1369
1370        Annotation[] getDeclaredAnnotations();
1371    }
1372
1373    public class PackageInfo extends Annotatable implements Info {
1374        private final String name;
1375        private final ClassInfo info;
1376        private final Package pkg;
1377
1378        public PackageInfo(Package pkg) {
1379            super(pkg);
1380            this.pkg = pkg;
1381            this.name = pkg.getName();
1382            this.info = null;
1383        }
1384
1385        public PackageInfo(String name) {
1386            info = new ClassInfo(name, null);
1387            this.name = name;
1388            this.pkg = null;
1389        }
1390
1391        public String getName() {
1392            return name;
1393        }
1394
1395        public Package get() throws ClassNotFoundException {
1396            return (pkg != null) ? pkg : info.get().getPackage();
1397        }
1398
1399        @Override
1400        public boolean equals(Object o) {
1401            if (this == o) return true;
1402            if (o == null || getClass() != o.getClass()) return false;
1403
1404            PackageInfo that = (PackageInfo) o;
1405
1406            if (name != null ? !name.equals(that.name) : that.name != null) return false;
1407
1408            return true;
1409        }
1410
1411        @Override
1412        public int hashCode() {
1413            return name != null ? name.hashCode() : 0;
1414        }
1415    }
1416
1417    public class ClassInfo extends Annotatable implements Info {
1418        private String name;
1419        private final List<MethodInfo> methods = new SingleLinkedList<MethodInfo>();
1420        private final List<MethodInfo> constructors = new SingleLinkedList<MethodInfo>();
1421        private String superType;
1422        private ClassInfo superclassInfo;
1423        private final List<ClassInfo> subclassInfos = new SingleLinkedList<ClassInfo>();
1424        private final List<String> interfaces = new SingleLinkedList<String>();
1425        private final List<FieldInfo> fields = new SingleLinkedList<FieldInfo>();
1426        private Class<?> clazz;
1427
1428
1429        public ClassInfo(Class clazz) {
1430            super(clazz);
1431            this.clazz = clazz;
1432            this.name = clazz.getName();
1433            final Class superclass = clazz.getSuperclass();
1434            this.superType = superclass != null ? superclass.getName() : null;
1435            for (Class intrface : clazz.getInterfaces()) {
1436                this.interfaces.add(intrface.getName());
1437            }
1438        }
1439
1440        public ClassInfo(final String name, final String superType) {
1441            this.name = name;
1442            this.superType = superType;
1443        }
1444
1445        @Override
1446        public String getMetaAnnotationName() {
1447            for (AnnotationInfo info : getAnnotations()) {
1448                for (Class<? extends Annotation> metaroot : metaroots) {
1449                    if (info.getName().equals(metaroot.getName())) return name;
1450                }
1451            }
1452
1453            if (name.endsWith("$$")) {
1454                ClassInfo info = classInfos.get(name.substring(0, name.length() - 2));
1455                if (info != null) {
1456                    return info.getMetaAnnotationName();
1457                }
1458            }
1459
1460            return null;
1461        }
1462
1463        public String getPackageName() {
1464            return name.indexOf(".") > 0 ? name.substring(0, name.lastIndexOf(".")) : "";
1465        }
1466
1467        public List<MethodInfo> getConstructors() {
1468            return constructors;
1469        }
1470
1471        public List<String> getInterfaces() {
1472            return interfaces;
1473        }
1474
1475        public List<FieldInfo> getFields() {
1476            return fields;
1477        }
1478
1479        public List<MethodInfo> getMethods() {
1480            return methods;
1481        }
1482
1483        public String getName() {
1484            return name;
1485        }
1486
1487        public String getSuperType() {
1488            return superType;
1489        }
1490
1491        public boolean isAnnotation() {
1492            return "java.lang.Object".equals(superType) && interfaces.size() == 1 && "java.lang.annotation.Annotation".equals(interfaces.get(0));
1493        }
1494
1495        public Class<?> get() throws ClassNotFoundException {
1496            if (clazz != null) return clazz;
1497            try {
1498                String fixedName = name.replaceFirst("<.*>", "");
1499                this.clazz = archive.loadClass(fixedName);
1500                return clazz;
1501            } catch (ClassNotFoundException notFound) {
1502                classesNotLoaded.add(name);
1503                throw notFound;
1504            }
1505        }
1506
1507        public String toString() {
1508            return name;
1509        }
1510    }
1511
1512    public class MethodInfo extends Annotatable implements Info {
1513        private final ClassInfo declaringClass;
1514        private final String descriptor;
1515        private final String name;
1516        private final List<List<AnnotationInfo>> parameterAnnotations = new LinkedList<List<AnnotationInfo>>();
1517        private final List<ParameterInfo> parameters = new SingleLinkedList<ParameterInfo>();
1518        private Member method;
1519
1520        public MethodInfo(ClassInfo info, Constructor constructor) {
1521            super(constructor);
1522            this.declaringClass = info;
1523            this.name = "<init>";
1524            this.descriptor = Type.getConstructorDescriptor(constructor);
1525        }
1526
1527        public MethodInfo(ClassInfo info, Method method) {
1528            super(method);
1529            this.declaringClass = info;
1530            this.name = method.getName();
1531            this.descriptor = Type.getMethodDescriptor(method);
1532            this.method = method;
1533        }
1534
1535        public MethodInfo(ClassInfo declarignClass, String name, String descriptor) {
1536            this.declaringClass = declarignClass;
1537            this.name = name;
1538            this.descriptor = descriptor;
1539        }
1540
1541        public String getDescriptor() {
1542            return descriptor;
1543        }
1544
1545        @Override
1546        public String getMetaAnnotationName() {
1547            return declaringClass.getMetaAnnotationName();
1548        }
1549
1550        @Override
1551        public Annotation[] getDeclaredAnnotations() {
1552            super.getDeclaredAnnotations();
1553            try {
1554                return ((AnnotatedElement) get()).getDeclaredAnnotations();
1555            } catch (ClassNotFoundException e) {
1556                return super.getDeclaredAnnotations();
1557            }
1558        }
1559
1560        public boolean isConstructor() {
1561            return getName().equals("<init>");
1562        }
1563
1564        public List<List<AnnotationInfo>> getParameterAnnotations() {
1565            return parameterAnnotations;
1566        }
1567
1568        public List<AnnotationInfo> getParameterAnnotations(int index) {
1569            if (index >= parameterAnnotations.size()) {
1570                for (int i = parameterAnnotations.size(); i <= index; i++) {
1571                    List<AnnotationInfo> annotationInfos = new LinkedList<AnnotationInfo>();
1572                    parameterAnnotations.add(i, annotationInfos);
1573                }
1574            }
1575            return parameterAnnotations.get(index);
1576        }
1577
1578        public List<ParameterInfo> getParameters() {
1579            return parameters;
1580        }
1581
1582        public String getName() {
1583            return name;
1584        }
1585
1586        public ClassInfo getDeclaringClass() {
1587            return declaringClass;
1588        }
1589
1590        public String toString() {
1591            return declaringClass + "@" + name;
1592        }
1593
1594        public Member get() throws ClassNotFoundException {
1595            if (method == null) {
1596                method = toMethod();
1597            }
1598
1599            return method;
1600        }
1601
1602        private Member toMethod() throws ClassNotFoundException {
1603            org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method(name, descriptor);
1604
1605            Class<?> clazz = this.declaringClass.get();
1606            List<Class> parameterTypes = new LinkedList<Class>();
1607
1608            for (Type type : method.getArgumentTypes()) {
1609                String paramType = type.getClassName();
1610                try {
1611                    parameterTypes.add(Classes.forName(paramType, clazz.getClassLoader()));
1612                } catch (ClassNotFoundException cnfe) {
1613                    throw new IllegalStateException("Parameter class could not be loaded for type " + paramType, cnfe);
1614                }
1615            }
1616
1617            Class[] parameters = parameterTypes.toArray(new Class[parameterTypes.size()]);
1618
1619            IllegalStateException noSuchMethod = null;
1620            while (clazz != null) {
1621                try {
1622                    if (name.equals("<init>")) {
1623                        return clazz.getDeclaredConstructor(parameters);
1624                    } else {
1625                        return clazz.getDeclaredMethod(name, parameters);
1626                    }
1627                } catch (NoSuchMethodException e) {
1628                    if (noSuchMethod == null) {
1629                        noSuchMethod = new IllegalStateException("Callback method does not exist: " + clazz.getName() + "." + name, e);
1630                    }
1631                    clazz = clazz.getSuperclass();
1632                }
1633            }
1634
1635            throw noSuchMethod;
1636        }
1637
1638    }
1639
1640    public class ParameterInfo extends Annotatable implements Info {
1641        private final MethodInfo declaringMethod;
1642        private final int index;
1643        private final List<AnnotationInfo> annotations = new LinkedList<AnnotationInfo>();
1644        private Parameter<?> parameter;
1645
1646        public ParameterInfo(MethodInfo parent, int index) {
1647            super();
1648            this.declaringMethod = parent;
1649            this.index = index;
1650        }
1651
1652        public ParameterInfo(MethodInfo parent, Parameter<?> parameter) {
1653            super(parameter);
1654            this.declaringMethod = parent;
1655            this.index = parameter.getIndex();
1656            this.parameter = parameter;
1657        }
1658
1659        public String getName() {
1660            return Integer.toString(index);
1661        }
1662
1663        public Parameter<?> get() throws ClassNotFoundException {
1664            if (parameter == null) {
1665                Member member = declaringMethod.get();
1666                if (member instanceof Method) {
1667                    parameter = Parameter.declaredBy((Method) member, index);
1668                } else if (member instanceof Constructor<?>) {
1669                    parameter = Parameter.declaredBy((Constructor<?>) member, index);
1670
1671                }
1672            }
1673            return parameter;
1674        }
1675
1676        @Override
1677        public Annotation[] getDeclaredAnnotations() {
1678            try {
1679                return get().getDeclaredAnnotations();
1680            } catch (ClassNotFoundException e) {
1681                return super.getDeclaredAnnotations();
1682            }
1683        }
1684
1685        public MethodInfo getDeclaringMethod() {
1686            return declaringMethod;
1687        }
1688
1689        @Override
1690        public String toString() {
1691            return String.format("%s(arg%s)", declaringMethod, index);
1692        }
1693    }
1694
1695    public class FieldInfo extends Annotatable implements Info {
1696        private final String name;
1697        private final String type;
1698        private final ClassInfo declaringClass;
1699        private Field field;
1700
1701        public FieldInfo(ClassInfo info, Field field) {
1702            super(field);
1703            this.declaringClass = info;
1704            this.name = field.getName();
1705            this.type = Type.getType(field.getType()).getDescriptor();
1706            this.field = field;
1707        }
1708
1709        public FieldInfo(ClassInfo declaringClass, String name, String type) {
1710            this.declaringClass = declaringClass;
1711            this.name = name;
1712            this.type = type;
1713        }
1714
1715        public String getName() {
1716            return name;
1717        }
1718
1719        public ClassInfo getDeclaringClass() {
1720            return declaringClass;
1721        }
1722
1723        public String getType() { // if this method starts to be used internally move this to constructors and just return type
1724            final Type t = Type.getType(type);
1725            if (t.getClassName() == null) {
1726                return t.getDescriptor();
1727            }
1728            return t.getClassName();
1729        }
1730
1731        public String toString() {
1732            return declaringClass + "#" + name;
1733        }
1734
1735        @Override
1736        public String getMetaAnnotationName() {
1737            return declaringClass.getMetaAnnotationName();
1738        }
1739
1740        @Override
1741        public Annotation[] getDeclaredAnnotations() {
1742            super.getDeclaredAnnotations();
1743            try {
1744                return ((AnnotatedElement) get()).getDeclaredAnnotations();
1745            } catch (ClassNotFoundException e) {
1746                return super.getDeclaredAnnotations();
1747            }
1748        }
1749
1750        public Member get() throws ClassNotFoundException {
1751            if (field == null) {
1752                field = toField();
1753            }
1754
1755            return field;
1756        }
1757
1758        private Field toField() throws ClassNotFoundException {
1759
1760            Class<?> clazz = this.declaringClass.get();
1761
1762            try {
1763                return clazz.getDeclaredField(name);
1764            } catch (NoSuchFieldException e) {
1765                throw new IllegalStateException(name, e);
1766            }
1767
1768        }
1769    }
1770
1771    public class AnnotationInfo extends Annotatable implements Info {
1772        private final String name;
1773
1774        public AnnotationInfo(Annotation annotation) {
1775            this(Type.getType(annotation.annotationType()).getDescriptor());
1776        }
1777
1778        public AnnotationInfo(Class<? extends Annotation> annotation) {
1779            this.name = annotation.getName().intern();
1780        }
1781
1782        public AnnotationInfo(String name) {
1783            final Type type = Type.getType(name);
1784            name = type.getClassName();
1785            if (name == null) {
1786                name = type.getDescriptor(); // name was already a class name
1787            }
1788            this.name = name;
1789        }
1790
1791        public String getName() {
1792            return name;
1793        }
1794
1795        public String toString() {
1796            return name;
1797        }
1798    }
1799
1800    private void index(AnnotationInfo annotationInfo, Info info) {
1801        initAnnotationInfos(annotationInfo.getName()).add(info);
1802    }
1803
1804    public class InfoBuildingVisitor extends EmptyVisitor {
1805        private Info info;
1806
1807        public InfoBuildingVisitor() {
1808        }
1809
1810        public InfoBuildingVisitor(Info info) {
1811            this.info = info;
1812        }
1813
1814        public Info getInfo() {
1815            return info;
1816        }
1817
1818        @Override
1819        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
1820            if (name.endsWith("package-info")) {
1821                info = new PackageInfo(javaName(name));
1822            } else {
1823
1824                ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName));
1825
1826//                if (signature == null) {
1827                for (final String interfce : interfaces) {
1828                    classInfo.interfaces.add(javaName(interfce));
1829                }
1830//                } else {
1831//                    // the class uses generics
1832//                    new SignatureReader(signature).accept(new GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE.CLASS, classInfo));
1833//                }
1834                info = classInfo;
1835                classInfos.put(classInfo.getName(), classInfo);
1836            }
1837        }
1838
1839        @Override
1840        public void visitEnd() {
1841            super.visitEnd();
1842            if (cleanOnNaked()) {
1843                if (ClassInfo.class.isInstance(info) && isNaked(ClassInfo.class.cast(info))) {
1844                    classInfos.remove(info.getName());
1845                } else if (PackageInfo.class.isInstance(info) && isNaked(PackageInfo.class.cast(info))) {
1846                    classInfos.remove(info.getName());
1847                }
1848            }
1849        }
1850
1851        private boolean isNaked(final PackageInfo info) {
1852            return info.getAnnotations().isEmpty();
1853        }
1854
1855        private boolean isNaked(final ClassInfo info) {
1856            if (!info.getAnnotations().isEmpty()) {
1857                return false;
1858            }
1859            for (final FieldInfo fieldInfo : info.getFields()) {
1860                if (!fieldInfo.getAnnotations().isEmpty()) {
1861                    return false;
1862                }
1863            }
1864            for (final MethodInfo methodInfo : info.getMethods()) {
1865                if (!methodInfo.getAnnotations().isEmpty()) {
1866                    return false;
1867                }
1868            }
1869            return true;
1870        }
1871
1872        private String javaName(String name) {
1873            return (name == null) ? null : name.replace('/', '.');
1874        }
1875
1876        @Override
1877        public void visitInnerClass(String name, String outerName, String innerName, int access) {
1878            super.visitInnerClass(name, outerName, innerName, access);
1879        }
1880
1881        @Override
1882        public void visitAttribute(Attribute attribute) {
1883            super.visitAttribute(attribute);
1884        }
1885
1886        @Override
1887        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
1888            if (isTracked(desc)) {
1889                AnnotationInfo annotationInfo = new AnnotationInfo(desc);
1890                info.getAnnotations().add(annotationInfo);
1891                index(annotationInfo, info);
1892                return new InfoBuildingVisitor(annotationInfo).annotationVisitor();
1893            }
1894            return super.visitAnnotation(desc, visible);
1895        }
1896
1897        @Override
1898        public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
1899            ClassInfo classInfo = ((ClassInfo) info);
1900            FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc);
1901            classInfo.getFields().add(fieldInfo);
1902            return new InfoBuildingVisitor(fieldInfo).fieldVisitor();
1903        }
1904
1905        @Override
1906        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
1907            ClassInfo classInfo = ((ClassInfo) info);
1908            MethodInfo methodInfo = new MethodInfo(classInfo, name, desc);
1909
1910            classInfo.getMethods().add(methodInfo);
1911            return new InfoBuildingVisitor(methodInfo).methodVisitor();
1912        }
1913
1914
1915        @Override
1916        public AnnotationVisitor visitMethodParameterAnnotation(int param, String desc, boolean visible) {
1917            if (isTracked(desc)) {
1918                MethodInfo methodInfo = ((MethodInfo) info);
1919                List<AnnotationInfo> annotationInfos = methodInfo.getParameterAnnotations(param);
1920                AnnotationInfo annotationInfo = new AnnotationInfo(desc);
1921                annotationInfos.add(annotationInfo);
1922
1923                ParameterInfo parameterInfo = new ParameterInfo(methodInfo, param);
1924                methodInfo.getParameters().add(parameterInfo);
1925                index(annotationInfo, parameterInfo);
1926                return new InfoBuildingVisitor(annotationInfo).annotationVisitor();
1927            }
1928            return super.visitMethodParameterAnnotation(param, desc, visible);
1929        }
1930    }
1931}