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