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