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