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 }