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    
021    package org.apache.xbean.finder;
022    
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.lang.annotation.Annotation;
026    import java.lang.reflect.AnnotatedElement;
027    import java.lang.reflect.Constructor;
028    import java.lang.reflect.Field;
029    import java.lang.reflect.Method;
030    import java.net.URL;
031    import java.util.ArrayList;
032    import java.util.Collections;
033    import java.util.HashMap;
034    import java.util.List;
035    import java.util.Map;
036    
037    import org.apache.xbean.finder.util.SingleLinkedList;
038    import org.objectweb.asm.AnnotationVisitor;
039    import org.objectweb.asm.ClassReader;
040    import org.objectweb.asm.FieldVisitor;
041    import org.objectweb.asm.MethodVisitor;
042    import org.objectweb.asm.commons.EmptyVisitor;
043    import org.objectweb.asm.signature.SignatureVisitor;
044    
045    /**
046     * @version $Rev: 1144600 $ $Date: 2011-07-09 14:13:25 +0800 (Sat, 09 Jul 2011) $
047     */
048    public abstract class AbstractFinder implements IAnnotationFinder {
049        private final Map<String, List<Info>> annotated = new HashMap<String, List<Info>>();
050        protected final Map<String, ClassInfo> classInfos = new HashMap<String, ClassInfo>();
051        protected final Map<String, ClassInfo> originalInfos = new HashMap<String, ClassInfo>();
052        private final List<String> classesNotLoaded = new ArrayList<String>();
053        private final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES;
054    
055        protected abstract URL getResource(String className);
056    
057        protected abstract Class<?> loadClass(String fixedName) throws ClassNotFoundException;
058    
059        public List<String> getAnnotatedClassNames() {
060            return new ArrayList<String>(originalInfos.keySet());
061        }
062    
063        /**
064         * The link() method must be called to successfully use the findSubclasses and findImplementations methods
065         * @return
066         * @throws IOException
067         */
068        public AbstractFinder link() throws IOException {
069            // already linked?
070            if (originalInfos.size() > 0) return this;
071    
072            // keep track of what was originally from the archives
073            originalInfos.putAll(classInfos);
074    
075            for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
076    
077                linkParent(classInfo);
078            }
079    
080            for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
081    
082                linkInterfaces(classInfo);
083            }
084    
085            return this;
086        }
087    
088        private void linkParent(ClassInfo classInfo) throws IOException {
089            if (classInfo.superType == null) return;
090            if (classInfo.superType.equals("java.lang.Object")) return;
091            
092            ClassInfo parentInfo = classInfo.superclassInfo;
093    
094            if (parentInfo == null) {
095    
096                parentInfo = classInfos.get(classInfo.superType);
097    
098                if (parentInfo == null) {
099    
100                    if (classInfo.clazz != null) {
101                        readClassDef(((Class<?>) classInfo.clazz).getSuperclass());
102                    } else {
103                        readClassDef(classInfo.superType);
104                    }
105    
106                    parentInfo = classInfos.get(classInfo.superType);
107    
108                    if (parentInfo == null) return;
109                    
110                    linkParent(parentInfo);
111                }
112    
113                classInfo.superclassInfo = parentInfo;
114            }
115    
116            if (!parentInfo.subclassInfos.contains(classInfo)) {
117                parentInfo.subclassInfos.add(classInfo);
118            }
119        }
120    
121        private void linkInterfaces(ClassInfo classInfo) throws IOException {
122            final List<ClassInfo> infos = new ArrayList<ClassInfo>();
123    
124            if (classInfo.clazz != null){
125                final Class<?>[] interfaces = classInfo.clazz.getInterfaces();
126    
127                for (Class<?> clazz : interfaces) {
128                    ClassInfo interfaceInfo = classInfos.get(clazz.getName());
129    
130                    if (interfaceInfo == null){
131                        readClassDef(clazz);
132                    }
133    
134                    interfaceInfo = classInfos.get(clazz.getName());
135    
136                    if (interfaceInfo != null) {
137                        infos.add(interfaceInfo);
138                    }
139                }
140            } else {
141                for (String className : classInfo.interfaces) {
142                    ClassInfo interfaceInfo = classInfos.get(className);
143    
144                    if (interfaceInfo == null){
145                        readClassDef(className);
146                    }
147    
148                    interfaceInfo = classInfos.get(className);
149    
150                    if (interfaceInfo != null) {
151                        infos.add(interfaceInfo);
152                    }
153                }
154            }
155    
156            for (ClassInfo info : infos) {
157                linkInterfaces(info);
158            }
159        }
160    
161        public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
162            List<Info> infos = annotated.get(annotation.getName());
163            return infos != null && !infos.isEmpty();
164        }
165    
166        /**
167         * Returns a list of classes that could not be loaded in last invoked findAnnotated* method.
168         * <p/>
169         * The list will only contain entries of classes whose byte code matched the requirements
170         * of last invoked find* method, but were unable to be loaded and included in the results.
171         * <p/>
172         * The list returned is unmodifiable.  Once obtained, the returned list will be a live view of the
173         * results from the last findAnnotated* method call.
174         * <p/>
175         * This method is not thread safe.
176         * @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call.
177         */
178        public List<String> getClassesNotLoaded() {
179            return Collections.unmodifiableList(classesNotLoaded);
180        }
181    
182        public List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation) {
183            classesNotLoaded.clear();
184            List<Package> packages = new ArrayList<Package>();
185            List<Info> infos = getAnnotationInfos(annotation.getName());
186            for (Info info : infos) {
187                if (info instanceof PackageInfo) {
188                    PackageInfo packageInfo = (PackageInfo) info;
189                    try {
190                        Package pkg = packageInfo.get();
191                        // double check via proper reflection
192                        if (pkg.isAnnotationPresent(annotation)) {
193                            packages.add(pkg);
194                        }
195                    } catch (ClassNotFoundException e) {
196                        classesNotLoaded.add(packageInfo.getName());
197                    }
198                }
199            }
200            return packages;
201        }
202    
203        public List<Class<?>> findAnnotatedClasses(Class<? extends Annotation> annotation) {
204            classesNotLoaded.clear();
205            List<Class<?>> classes = new ArrayList<Class<?>>();
206            List<Info> infos = getAnnotationInfos(annotation.getName());
207            for (Info info : infos) {
208                if (info instanceof ClassInfo) {
209                    ClassInfo classInfo = (ClassInfo) info;
210                    try {
211                        Class clazz = classInfo.get();
212                        // double check via proper reflection
213                        if (clazz.isAnnotationPresent(annotation)) {
214                            classes.add(clazz);
215                        }
216                    } catch (ClassNotFoundException e) {
217                        classesNotLoaded.add(classInfo.getName());
218                    }
219                }
220            }
221            return classes;
222        }
223    
224        @Override
225        public List<Annotated<Class<?>>> findMetaAnnotatedClasses(Class<? extends Annotation> annotation) {
226            List<Class<?>> classes = findAnnotatedClasses(annotation);
227            List<Annotated<Class<?>>> list = new ArrayList<Annotated<Class<?>>>();
228            for (final Class<?> clazz : classes) {
229                list.add(new MetaAnnotatedClass(clazz));
230            }
231            return list;
232        }
233    
234        /**
235         * Naive implementation - works extremelly slow O(n^3)
236         *
237         * @param annotation
238         * @return list of directly or indirectly (inherited) annotated classes
239         */
240        public List<Class<?>> findInheritedAnnotatedClasses(Class<? extends Annotation> annotation) {
241            classesNotLoaded.clear();
242            List<Class<?>> classes = new ArrayList<Class<?>>();
243            List<Info> infos = getAnnotationInfos(annotation.getName());
244            for (Info info : infos) {
245                try {
246                    if(info instanceof ClassInfo){
247                       classes.add(((ClassInfo) info).get());
248                    }
249                } catch (ClassNotFoundException cnfe) {
250                    // TODO: ignored, but a log message would be appropriate
251                }
252            }
253            boolean annClassFound;
254            List<ClassInfo> tempClassInfos = new ArrayList<ClassInfo>(classInfos.values());
255            do {
256                annClassFound = false;
257                for (int pos = 0; pos < tempClassInfos.size(); pos++) {
258                    ClassInfo classInfo = tempClassInfos.get(pos);
259                    try {
260                        // check whether any superclass is annotated
261                        String superType = classInfo.getSuperType();
262                        for (Class clazz : classes) {
263                            if (superType.equals(clazz.getName())) {
264                                classes.add(classInfo.get());
265                                tempClassInfos.remove(pos);
266                                annClassFound = true;
267                                break;
268                            }
269                        }
270                        // check whether any interface is annotated
271                        List<String> interfces = classInfo.getInterfaces();
272                        for (String interfce: interfces) {
273                            for (Class clazz : classes) {
274                                if (interfce.replaceFirst("<.*>","").equals(clazz.getName())) {
275                                    classes.add(classInfo.get());
276                                    tempClassInfos.remove(pos);
277                                    annClassFound = true;
278                                    break;
279                                }
280                            }
281                        }
282                    } catch (ClassNotFoundException e) {
283                        classesNotLoaded.add(classInfo.getName());
284                    } catch (NoClassDefFoundError e) {
285                        classesNotLoaded.add(classInfo.getName());
286                    }
287                }
288            } while (annClassFound);
289            return classes;
290        }
291    
292        public List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation) {
293            classesNotLoaded.clear();
294            List<ClassInfo> seen = new ArrayList<ClassInfo>();
295            List<Method> methods = new ArrayList<Method>();
296            List<Info> infos = getAnnotationInfos(annotation.getName());
297            for (Info info : infos) {
298                if (info instanceof MethodInfo && !info.getName().equals("<init>")) {
299                    MethodInfo methodInfo = (MethodInfo) info;
300                    ClassInfo classInfo = methodInfo.getDeclaringClass();
301    
302                    if (seen.contains(classInfo)) continue;
303    
304                    seen.add(classInfo);
305    
306                    try {
307                        Class clazz = classInfo.get();
308                        for (Method method : clazz.getDeclaredMethods()) {
309                            if (method.isAnnotationPresent(annotation)) {
310                                methods.add(method);
311                            }
312                        }
313                    } catch (ClassNotFoundException e) {
314                        classesNotLoaded.add(classInfo.getName());
315                    }
316                }
317            }
318            return methods;
319        }
320    
321        @Override
322        public List<Annotated<Method>> findMetaAnnotatedMethods(Class<? extends Annotation> annotation) {
323            List<Method> methods = findAnnotatedMethods(annotation);
324            List<Annotated<Method>> list = new ArrayList<Annotated<Method>>();
325            for (final Method method : methods) {
326                list.add(new MetaAnnotatedMethod(method));
327            }
328            return list;
329        }
330    
331        public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
332            classesNotLoaded.clear();
333            List<ClassInfo> seen = new ArrayList<ClassInfo>();
334            List<Constructor> constructors = new ArrayList<Constructor>();
335            List<Info> infos = getAnnotationInfos(annotation.getName());
336            for (Info info : infos) {
337                if (info instanceof MethodInfo && info.getName().equals("<init>")) {
338                    MethodInfo methodInfo = (MethodInfo) info;
339                    ClassInfo classInfo = methodInfo.getDeclaringClass();
340    
341                    if (seen.contains(classInfo)) continue;
342    
343                    seen.add(classInfo);
344    
345                    try {
346                        Class clazz = classInfo.get();
347                        for (Constructor constructor : clazz.getConstructors()) {
348                            if (constructor.isAnnotationPresent(annotation)) {
349                                constructors.add(constructor);
350                            }
351                        }
352                    } catch (ClassNotFoundException e) {
353                        classesNotLoaded.add(classInfo.getName());
354                    }
355                }
356            }
357            return constructors;
358        }
359    
360        public List<Field> findAnnotatedFields(Class<? extends Annotation> annotation) {
361            classesNotLoaded.clear();
362            List<ClassInfo> seen = new ArrayList<ClassInfo>();
363            List<Field> fields = new ArrayList<Field>();
364            List<Info> infos = getAnnotationInfos(annotation.getName());
365            for (Info info : infos) {
366                if (info instanceof FieldInfo) {
367                    FieldInfo fieldInfo = (FieldInfo) info;
368                    ClassInfo classInfo = fieldInfo.getDeclaringClass();
369    
370                    if (seen.contains(classInfo)) continue;
371    
372                    seen.add(classInfo);
373    
374                    try {
375                        Class clazz = classInfo.get();
376                        for (Field field : clazz.getDeclaredFields()) {
377                            if (field.isAnnotationPresent(annotation)) {
378                                fields.add(field);
379                            }
380                        }
381                    } catch (ClassNotFoundException e) {
382                        classesNotLoaded.add(classInfo.getName());
383                    }
384                }
385            }
386            return fields;
387        }
388    
389        @Override
390        public List<Annotated<Field>> findMetaAnnotatedFields(Class<? extends Annotation> annotation) {
391            List<Field> fields = findAnnotatedFields(annotation);
392            List<Annotated<Field>> list = new ArrayList<Annotated<Field>>();
393            for (final Field field : fields) {
394                list.add(new MetaAnnotatedField(field));
395            }
396    
397            return list;
398        }
399    
400        public List<Class<?>> findClassesInPackage(String packageName, boolean recursive) {
401            classesNotLoaded.clear();
402            List<Class<?>> classes = new ArrayList<Class<?>>();
403            for (ClassInfo classInfo : classInfos.values()) {
404                try {
405                    if (recursive && classInfo.getPackageName().startsWith(packageName)){
406                        classes.add(classInfo.get());
407                    } else if (classInfo.getPackageName().equals(packageName)){
408                        classes.add(classInfo.get());
409                    }
410                } catch (ClassNotFoundException e) {
411                    classesNotLoaded.add(classInfo.getName());
412                }
413            }
414            return classes;
415        }
416    
417        public <T> List<Class<? extends T>> findSubclasses(Class<T> clazz) {
418            if (clazz == null) throw new NullPointerException("class cannot be null");
419    
420            classesNotLoaded.clear();
421    
422            final ClassInfo classInfo = classInfos.get(clazz.getName());
423    
424            List<Class<? extends T>> found = new ArrayList<Class<? extends T>>();
425    
426            if (classInfo == null) return found;
427    
428            findSubclasses(classInfo, found, clazz);
429    
430            return found;
431        }
432    
433        private <T> void findSubclasses(ClassInfo classInfo, List<Class<? extends T>> found, Class<T> clazz) {
434    
435            for (ClassInfo subclassInfo : classInfo.subclassInfos) {
436    
437                try {
438                    found.add(subclassInfo.get().asSubclass(clazz));
439                } catch (ClassNotFoundException e) {
440                    classesNotLoaded.add(subclassInfo.getName());
441                }
442    
443                findSubclasses(subclassInfo, found, clazz);
444            }
445        }
446    
447        private <T> List<Class<? extends T>> _findSubclasses(Class<T> clazz) {
448            if (clazz == null) throw new NullPointerException("class cannot be null");
449    
450            List<Class<? extends T>> classes = new ArrayList<Class<? extends T>>();
451    
452    
453            for (ClassInfo classInfo : classInfos.values()) {
454    
455                try {
456    
457                    if (clazz.getName().equals(classInfo.superType)) {
458    
459                        if (clazz.isAssignableFrom(classInfo.get())) {
460    
461                            classes.add(classInfo.get().asSubclass(clazz));
462    
463                            classes.addAll(_findSubclasses(classInfo.get().asSubclass(clazz)));
464                        }
465                    }
466    
467                } catch (ClassNotFoundException e) {
468                    classesNotLoaded.add(classInfo.getName());
469                }
470    
471            }
472    
473            return classes;
474        }
475    
476        public <T> List<Class<? extends T>> findImplementations(Class<T> clazz) {
477            if (clazz == null) throw new NullPointerException("class cannot be null");
478            if (!clazz.isInterface()) new IllegalArgumentException("class must be an interface");
479            classesNotLoaded.clear();
480    
481            final String interfaceName = clazz.getName();
482    
483            // Collect all interfaces extending the main interface (recursively)
484            // Collect all implementations of interfaces
485            // i.e. all *directly* implementing classes
486            List<ClassInfo> infos = collectImplementations(interfaceName);
487    
488            // Collect all subclasses of implementations
489            List<Class<? extends T>> classes = new ArrayList<Class<? extends T>>();
490            for (ClassInfo info : infos) {
491                try {
492                    final Class<? extends T> impl = (Class<? extends T>) info.get();
493    
494                    if (clazz.isAssignableFrom(impl)) {
495                        classes.add(impl);
496    
497                        // Optimization: Don't need to call this method if parent class was already searched
498    
499    
500    
501                        classes.addAll(_findSubclasses(impl));
502                    }
503    
504                } catch (ClassNotFoundException e) {
505                    classesNotLoaded.add(info.getName());
506                }
507            }
508            return classes;
509        }
510    
511        private List<ClassInfo> collectImplementations(String interfaceName) {
512            final List<ClassInfo> infos = new ArrayList<ClassInfo>();
513    
514            for (ClassInfo classInfo : classInfos.values()) {
515    
516                if (classInfo.interfaces.contains(interfaceName)) {
517    
518                    infos.add(classInfo);
519    
520                    try {
521    
522                        final Class clazz = classInfo.get();
523    
524                        if (clazz.isInterface() && !clazz.isAnnotation()) {
525    
526                            infos.addAll(collectImplementations(classInfo.name));
527    
528                        }
529                        
530                    } catch (ClassNotFoundException ignore) {
531                        // we'll deal with this later
532                    }
533                }
534            }
535            return infos;
536        }
537    
538        protected List<Info> getAnnotationInfos(String name) {
539            List<Info> infos = annotated.get(name);
540            if (infos == null) {
541                infos = new SingleLinkedList<Info>();
542                annotated.put(name, infos);
543            }
544            return infos;
545        }
546    
547        protected void readClassDef(String className) {
548            int pos = className.indexOf("<");
549            if (pos > -1) {
550                className = className.substring(0, pos);
551            }
552            pos = className.indexOf(">");
553            if (pos > -1) {
554                className = className.substring(0, pos);
555            }
556            if (!className.endsWith(".class")) {
557                className = className.replace('.', '/') + ".class";
558            }
559            try {
560                URL resource = getResource(className);
561                if (resource != null) {
562                    InputStream in = resource.openStream();
563                    try {
564                        readClassDef(in);
565                    } finally {
566                        in.close();
567                    }
568                } else {
569                    classesNotLoaded.add(className + " (no resource found for class)");
570                }
571            } catch (IOException e) {
572                classesNotLoaded.add(className + e.getMessage());
573            }
574    
575        }
576    
577        protected void readClassDef(InputStream in) throws IOException {
578            readClassDef(in, null);
579        }
580    
581        protected void readClassDef(InputStream in, String path) throws IOException {
582            ClassReader classReader = new ClassReader(in);
583            classReader.accept(new InfoBuildingVisitor(path), ASM_FLAGS);
584        }
585    
586        protected void readClassDef(Class clazz) {
587            List<Info> infos = new ArrayList<Info>();
588    
589            Package aPackage = clazz.getPackage();
590            if (aPackage != null){
591                final PackageInfo info = new PackageInfo(aPackage);
592                for (AnnotationInfo annotation : info.getAnnotations()) {
593                    List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
594                    if (!annotationInfos.contains(info)) {
595                        annotationInfos.add(info);
596                    }
597                }
598            }
599    
600            ClassInfo classInfo = new ClassInfo(clazz);
601            infos.add(classInfo);
602            classInfos.put(clazz.getName(), classInfo);
603            for (Method method : clazz.getDeclaredMethods()) {
604                infos.add(new MethodInfo(classInfo, method));
605            }
606    
607            for (Constructor constructor : clazz.getConstructors()) {
608                infos.add(new MethodInfo(classInfo, constructor));
609            }
610    
611            for (Field field : clazz.getDeclaredFields()) {
612                infos.add(new FieldInfo(classInfo, field));
613            }
614    
615            for (Info info : infos) {
616                for (AnnotationInfo annotation : info.getAnnotations()) {
617                    List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
618                    annotationInfos.add(info);
619                }
620            }
621        }
622    
623        public class Annotatable {
624            private final List<AnnotationInfo> annotations = new ArrayList<AnnotationInfo>();
625    
626            public Annotatable(AnnotatedElement element) {
627                for (Annotation annotation : getAnnotations(element)) {
628                    annotations.add(new AnnotationInfo(annotation.annotationType().getName()));
629                }
630            }
631    
632            public Annotatable() {
633            }
634    
635            public List<AnnotationInfo> getAnnotations() {
636                return annotations;
637            }
638            
639            /**
640             * Utility method to get around some errors caused by 
641             * interactions between the Equinox class loaders and 
642             * the OpenJPA transformation process.  There is a window 
643             * where the OpenJPA transformation process can cause 
644             * an annotation being processed to get defined in a 
645             * classloader during the actual defineClass call for 
646             * that very class (e.g., recursively).  This results in 
647             * a LinkageError exception.  If we see one of these, 
648             * retry the request.  Since the annotation will be 
649             * defined on the second pass, this should succeed.  If 
650             * we get a second exception, then it's likely some 
651             * other problem. 
652             * 
653             * @param element The AnnotatedElement we need information for.
654             * 
655             * @return An array of the Annotations defined on the element. 
656             */
657            private Annotation[] getAnnotations(AnnotatedElement element) {
658                try {
659                    return element.getAnnotations();
660                } catch (LinkageError e) {
661                    return element.getAnnotations();
662                }
663            }
664    
665        }
666    
667        public static interface Info {
668            String getName();
669    
670            List<AnnotationInfo> getAnnotations();
671        }
672    
673        public class PackageInfo extends Annotatable implements Info {
674            private final String name;
675            private final ClassInfo info;
676            private final Package pkg;
677    
678            public PackageInfo(Package pkg){
679                super(pkg);
680                this.pkg = pkg;
681                this.name = pkg.getName();
682                this.info = null;
683            }
684    
685            public PackageInfo(String name) {
686                info = new ClassInfo(name, null);
687                this.name = name;
688                this.pkg = null;
689            }
690    
691            public String getName() {
692                return name;
693            }
694    
695            public Package get() throws ClassNotFoundException {
696                return (pkg != null)?pkg:info.get().getPackage();
697            }
698    
699            @Override
700            public boolean equals(Object o) {
701                if (this == o) return true;
702                if (o == null || getClass() != o.getClass()) return false;
703    
704                PackageInfo that = (PackageInfo) o;
705    
706                if (name != null ? !name.equals(that.name) : that.name != null) return false;
707    
708                return true;
709            }
710    
711            @Override
712            public int hashCode() {
713                return name != null ? name.hashCode() : 0;
714            }
715        }
716    
717        public class ClassInfo extends Annotatable implements Info {
718            private String name;
719            private final List<MethodInfo> methods = new SingleLinkedList<MethodInfo>();
720            private final List<MethodInfo> constructors = new SingleLinkedList<MethodInfo>();
721            private String superType;
722            private ClassInfo superclassInfo;
723            private final List<ClassInfo> subclassInfos = new SingleLinkedList<ClassInfo>();
724            private final List<String> interfaces = new SingleLinkedList<String>();
725            private final List<FieldInfo> fields = new SingleLinkedList<FieldInfo>();
726            //e.g. bundle class path prefix.
727            private String path;
728            private Class<?> clazz;
729    
730            public ClassInfo(Class clazz) {
731                super(clazz);
732                this.clazz = clazz;
733                this.name = clazz.getName();
734                Class superclass = clazz.getSuperclass();
735                this.superType = superclass != null ? superclass.getName(): null;
736                for (Class intrface : clazz.getInterfaces()) {
737                    this.interfaces.add(intrface.getName());
738                }
739            }
740    
741            public ClassInfo(String name, String superType) {
742                this.name = name;
743                this.superType = superType;
744            }
745    
746            public String getPackageName(){
747                return name.indexOf(".") > 0 ? name.substring(0, name.lastIndexOf(".")) : "" ;
748            }
749    
750            public List<MethodInfo> getConstructors() {
751                return constructors;
752            }
753    
754            public List<String> getInterfaces() {
755                return interfaces;
756            }
757    
758            public List<FieldInfo> getFields() {
759                return fields;
760            }
761    
762            public List<MethodInfo> getMethods() {
763                return methods;
764            }
765    
766            public String getName() {
767                return name;
768            }
769    
770            public String getSuperType() {
771                return superType;
772            }
773    
774            public Class<?> get() throws ClassNotFoundException {
775                if (clazz != null) return clazz;
776                try {
777                    String fixedName = name.replaceFirst("<.*>", "");
778                    this.clazz = loadClass(fixedName);
779                    return clazz;
780                } catch (ClassNotFoundException notFound) {
781                    classesNotLoaded.add(name);
782                    throw notFound;
783                }
784            }
785    
786            public String toString() {
787                return name;
788            }
789    
790            public String getPath() {
791                return path;
792            }
793        }
794    
795        public class MethodInfo extends Annotatable implements Info {
796            private final ClassInfo declaringClass;
797            private final String returnType;
798            private final String name;
799            private final List<List<AnnotationInfo>> parameterAnnotations = new ArrayList<List<AnnotationInfo>>();
800    
801            public MethodInfo(ClassInfo info, Constructor constructor){
802                super(constructor);
803                this.declaringClass = info;
804                this.name = "<init>";
805                this.returnType = Void.TYPE.getName();
806            }
807    
808            public MethodInfo(ClassInfo info, Method method){
809                super(method);
810                this.declaringClass = info;
811                this.name = method.getName();
812                this.returnType = method.getReturnType().getName();
813            }
814    
815            public MethodInfo(ClassInfo declarignClass, String name, String returnType) {
816                this.declaringClass = declarignClass;
817                this.name = name;
818                this.returnType = returnType;
819            }
820    
821            public List<List<AnnotationInfo>> getParameterAnnotations() {
822                return parameterAnnotations;
823            }
824    
825            public List<AnnotationInfo> getParameterAnnotations(int index) {
826                if (index >= parameterAnnotations.size()) {
827                    for (int i = parameterAnnotations.size(); i <= index; i++) {
828                        List<AnnotationInfo> annotationInfos = new ArrayList<AnnotationInfo>();
829                        parameterAnnotations.add(i, annotationInfos);
830                    }
831                }
832                return parameterAnnotations.get(index);
833            }
834    
835            public String getName() {
836                return name;
837            }
838    
839            public ClassInfo getDeclaringClass() {
840                return declaringClass;
841            }
842    
843            public String getReturnType() {
844                return returnType;
845            }
846    
847            public String toString() {
848                return declaringClass + "@" + name;
849            }
850        }
851    
852        public class FieldInfo extends Annotatable implements Info {
853            private final String name;
854            private final String type;
855            private final ClassInfo declaringClass;
856    
857            public FieldInfo(ClassInfo info, Field field){
858                super(field);
859                this.declaringClass = info;
860                this.name = field.getName();
861                this.type = field.getType().getName();
862            }
863    
864            public FieldInfo(ClassInfo declaringClass, String name, String type) {
865                this.declaringClass = declaringClass;
866                this.name = name;
867                this.type = type;
868            }
869    
870            public String getName() {
871                return name;
872            }
873    
874            public ClassInfo getDeclaringClass() {
875                return declaringClass;
876            }
877    
878            public String getType() {
879                return type;
880            }
881    
882            public String toString() {
883                return declaringClass + "#" + name;
884            }
885        }
886    
887        public class AnnotationInfo extends Annotatable implements Info {
888            private final String name;
889    
890            public AnnotationInfo(Annotation annotation){
891                this(annotation.getClass().getName());
892            }
893    
894            public AnnotationInfo(Class<? extends Annotation> annotation) {
895                this.name = annotation.getName().intern();
896            }
897    
898            public AnnotationInfo(String name) {
899                name = name.replaceAll("^L|;$", "");
900                name = name.replace('/', '.');
901                this.name = name.intern();
902            }
903    
904            public String getName() {
905                return name;
906            }
907    
908            public String toString() {
909                return name;
910            }
911        }
912    
913        public class InfoBuildingVisitor extends EmptyVisitor {
914            private Info info;
915            private String path;
916    
917            public InfoBuildingVisitor(String path) {
918                this.path = path;
919            }
920    
921            public InfoBuildingVisitor(Info info) {
922                this.info = info;
923            }
924    
925            @Override
926            public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
927                if (name.endsWith("package-info")) {
928                    info = new PackageInfo(javaName(name));
929                } else {
930                    ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName));
931                    classInfo.path = path;
932    //                if (signature == null) {
933                        for (String interfce : interfaces) {
934                            classInfo.getInterfaces().add(javaName(interfce));
935                        }
936    //                } else {
937    //                    // the class uses generics
938    //                    new SignatureReader(signature).accept(new GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE.CLASS, classInfo));
939    //                }
940                    info = classInfo;
941                    classInfos.put(classInfo.getName(), classInfo);
942                }
943            }
944    
945            private String javaName(String name) {
946                return (name == null)? null:name.replace('/', '.');
947            }
948    
949            @Override
950            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
951                AnnotationInfo annotationInfo = new AnnotationInfo(desc);
952                info.getAnnotations().add(annotationInfo);
953                getAnnotationInfos(annotationInfo.getName()).add(info);
954                return new InfoBuildingVisitor(annotationInfo);
955            }
956    
957            @Override
958            public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
959                ClassInfo classInfo = ((ClassInfo) info);
960                FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc);
961                classInfo.getFields().add(fieldInfo);
962                return new InfoBuildingVisitor(fieldInfo);
963            }
964    
965            @Override
966            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
967                ClassInfo classInfo = ((ClassInfo) info);
968                MethodInfo methodInfo = new MethodInfo(classInfo, name, desc);
969                classInfo.getMethods().add(methodInfo);
970                return new InfoBuildingVisitor(methodInfo);
971            }
972    
973            @Override
974            public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) {
975                MethodInfo methodInfo = ((MethodInfo) info);
976                List<AnnotationInfo> annotationInfos = methodInfo.getParameterAnnotations(param);
977                AnnotationInfo annotationInfo = new AnnotationInfo(desc);
978                annotationInfos.add(annotationInfo);
979                return new InfoBuildingVisitor(annotationInfo);
980            }
981        }
982    
983        public static class GenericAwareInfoBuildingVisitor implements SignatureVisitor {
984    
985            public enum TYPE {
986                CLASS
987            }
988    
989            public enum STATE {
990                BEGIN, END, SUPERCLASS, INTERFACE, FORMAL_TYPE_PARAM
991            }
992    
993            private Info info;
994            private GenericAwareInfoBuildingVisitor.TYPE type;
995            private GenericAwareInfoBuildingVisitor.STATE state;
996    
997            private static boolean debug = false;
998    
999            public GenericAwareInfoBuildingVisitor() {
1000            }
1001    
1002            public GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE type, Info info) {
1003                this.type = type;
1004                this.info = info;
1005                this.state = GenericAwareInfoBuildingVisitor.STATE.BEGIN;
1006            }
1007    
1008            public void visitFormalTypeParameter(String s) {
1009                if (debug) System.out.println(" s=" + s);
1010                switch (state) {
1011                    case BEGIN:
1012                        ((ClassInfo) info).name += "<" + s;
1013                }
1014                state = GenericAwareInfoBuildingVisitor.STATE.FORMAL_TYPE_PARAM;
1015            }
1016    
1017            public SignatureVisitor visitClassBound() {
1018                if (debug) System.out.println(" visitClassBound()");
1019                return this;
1020            }
1021    
1022            public SignatureVisitor visitInterfaceBound() {
1023                if (debug) System.out.println(" visitInterfaceBound()");
1024                return this;
1025            }
1026    
1027            public SignatureVisitor visitSuperclass() {
1028                if (debug) System.out.println(" visitSuperclass()");
1029                state = GenericAwareInfoBuildingVisitor.STATE.SUPERCLASS;
1030                return this;
1031            }
1032    
1033            public SignatureVisitor visitInterface() {
1034                if (debug) System.out.println(" visitInterface()");
1035                ((ClassInfo) info).getInterfaces().add("");
1036                state = GenericAwareInfoBuildingVisitor.STATE.INTERFACE;
1037                return this;
1038            }
1039    
1040            public SignatureVisitor visitParameterType() {
1041                if (debug) System.out.println(" visitParameterType()");
1042                return this;
1043            }
1044    
1045            public SignatureVisitor visitReturnType() {
1046                if (debug) System.out.println(" visitReturnType()");
1047                return this;
1048            }
1049    
1050            public SignatureVisitor visitExceptionType() {
1051                if (debug) System.out.println(" visitExceptionType()");
1052                return this;
1053            }
1054    
1055            public void visitBaseType(char c) {
1056                if (debug) System.out.println(" visitBaseType(" + c + ")");
1057            }
1058    
1059            public void visitTypeVariable(String s) {
1060                if (debug) System.out.println(" visitTypeVariable(" + s + ")");
1061            }
1062    
1063            public SignatureVisitor visitArrayType() {
1064                if (debug) System.out.println(" visitArrayType()");
1065                return this;
1066            }
1067    
1068            public void visitClassType(String s) {
1069                if (debug) System.out.println(" visitClassType(" + s + ")");
1070                switch (state) {
1071                    case INTERFACE:
1072                        List<String> interfces = ((ClassInfo) info).getInterfaces();
1073                        int idx = interfces.size() - 1;
1074                        String interfce = interfces.get(idx);
1075                        if (interfce.length() == 0) {
1076                            interfce = javaName(s);
1077                        } else {
1078                            interfce += javaName(s);
1079                        }
1080                        interfces.set(idx, interfce);
1081                        break;
1082                    case SUPERCLASS:
1083                        if (!s.equals("java/lang/Object")) {
1084                            ((ClassInfo) info).superType = javaName(s);
1085                        }
1086                }
1087            }
1088    
1089            public void visitInnerClassType(String s) {
1090                if (debug) System.out.println(" visitInnerClassType(" + s + ")");
1091            }
1092    
1093            public void visitTypeArgument() {
1094                if (debug) System.out.println(" visitTypeArgument()");
1095                switch (state) {
1096                    case INTERFACE:
1097                        List<String> interfces = ((ClassInfo) info).getInterfaces();
1098                        int idx = interfces.size() - 1;
1099                        String interfce = interfces.get(idx);
1100                        interfce += "<";
1101                        interfces.set(idx, interfce);
1102                }
1103            }
1104    
1105            public SignatureVisitor visitTypeArgument(char c) {
1106                if (debug) System.out.println(" visitTypeArgument(" + c + ")");
1107                switch (state) {
1108                    case INTERFACE:
1109                        List<String> interfces = ((ClassInfo) info).getInterfaces();
1110                        int idx = interfces.size() - 1;
1111                        String interfce = interfces.get(idx);
1112                        interfce += "<";
1113                        interfces.set(idx, interfce);
1114                }
1115                return this;
1116            }
1117    
1118            public void visitEnd() {
1119                if (debug) System.out.println(" visitEnd()");
1120                switch (state) {
1121                    case INTERFACE:
1122                        List<String> interfces = ((ClassInfo) info).getInterfaces();
1123                        int idx = interfces.size() - 1;
1124                        String interfce = interfces.get(idx);
1125                        interfce += ">";
1126                        interfces.set(idx, interfce);
1127                        break;
1128                    case FORMAL_TYPE_PARAM:
1129                        String name = ((ClassInfo) info).name;
1130                        if (name.contains("<")) {
1131                            ((ClassInfo) info).name += ">";
1132                        }
1133                }
1134                state = GenericAwareInfoBuildingVisitor.STATE.END;
1135            }
1136    
1137            private String javaName(String name) {
1138                return (name == null)? null:name.replace('/', '.');
1139            }
1140    
1141        }
1142    }