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