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