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