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