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
021 package org.apache.xbean.finder;
022
023 import org.apache.xbean.finder.archive.Archive;
024 import org.apache.xbean.finder.util.Classes;
025 import org.apache.xbean.finder.util.SingleLinkedList;
026 import org.objectweb.asm.AnnotationVisitor;
027 import org.objectweb.asm.Attribute;
028 import org.objectweb.asm.ClassReader;
029 import org.objectweb.asm.FieldVisitor;
030 import org.objectweb.asm.MethodVisitor;
031 import org.objectweb.asm.Type;
032 import org.objectweb.asm.commons.EmptyVisitor;
033 import org.objectweb.asm.signature.SignatureReader;
034 import org.objectweb.asm.signature.SignatureVisitor;
035
036 import java.io.IOException;
037 import java.io.InputStream;
038 import java.lang.annotation.Annotation;
039 import java.lang.reflect.AnnotatedElement;
040 import java.lang.reflect.Constructor;
041 import java.lang.reflect.Field;
042 import java.lang.reflect.Member;
043 import java.lang.reflect.Method;
044 import java.util.ArrayList;
045 import java.util.Collections;
046 import java.util.HashMap;
047 import java.util.HashSet;
048 import java.util.List;
049 import java.util.Map;
050 import java.util.Set;
051
052 /**
053 * ClassFinder searches the classpath of the specified classloader for
054 * packages, classes, constructors, methods, or fields with specific annotations.
055 * <p/>
056 * For security reasons ASM is used to find the annotations. Classes are not
057 * loaded unless they match the requirements of a called findAnnotated* method.
058 * Once loaded, these classes are cached.
059 *
060 * @version $Rev: 1147744 $ $Date: 2011-07-18 12:59:28 +0800 (Mon, 18 Jul 2011) $
061 */
062 public class AnnotationFinder implements IAnnotationFinder {
063
064 private final Set<Class<? extends Annotation>> metaroots = new HashSet<Class<? extends Annotation>>();
065
066 private final Map<String, List<Info>> annotated = new HashMap<String, List<Info>>();
067
068 protected final Map<String, ClassInfo> classInfos = new HashMap<String, ClassInfo>();
069 protected final Map<String, ClassInfo> originalInfos = new HashMap<String, ClassInfo>();
070 private final List<String> classesNotLoaded = new ArrayList<String>();
071 private final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES;
072 private final Archive archive;
073
074 public AnnotationFinder(Archive archive) {
075 this.archive = archive;
076
077 for (String className : this.archive) {
078 try {
079 readClassDef(archive.getBytecode(className));
080 } catch (NoClassDefFoundError e) {
081 throw new NoClassDefFoundError("Could not fully load class: " + className + "\n due to:" + e.getMessage());
082 } catch (ClassNotFoundException e) {
083 e.printStackTrace();
084 } catch (IOException e) {
085 e.printStackTrace();
086 }
087 }
088 }
089
090 public List<String> getAnnotatedClassNames() {
091 return new ArrayList<String>(originalInfos.keySet());
092 }
093
094 public Archive getArchive() {
095 return archive;
096 }
097
098 /**
099 * The link() method must be called to successfully use the findSubclasses and findImplementations methods
100 *
101 * @return
102 * @throws java.io.IOException
103 */
104 public AnnotationFinder link() {
105 // already linked?
106 if (originalInfos.size() > 0) return this;
107
108 // keep track of what was originally from the archives
109 originalInfos.putAll(classInfos);
110
111 for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
112
113 linkParent(classInfo);
114 }
115
116 for (ClassInfo classInfo : classInfos.values().toArray(new ClassInfo[classInfos.size()])) {
117
118 linkInterfaces(classInfo);
119
120 }
121
122 // diff new and old lists
123 resolveAnnotations(new ArrayList<String>());
124
125 return this;
126 }
127
128 /**
129 * Used to support meta annotations
130 * <p/>
131 * Once the list of classes has been read from the Archive, we
132 * iterate over all the annotations that are used by those classes
133 * and recursively resolve any annotations those annotations use.
134 *
135 * @param scanned
136 * @throws ClassNotFoundException
137 * @throws IOException
138 */
139 private void resolveAnnotations(List<String> scanned) {
140
141 // Get a list of the annotations that exist before we start
142 List<String> annotations = new ArrayList<String>(annotated.keySet());
143
144 for (String annotation : annotations) {
145 if (scanned.contains(annotation)) continue;
146 readClassDef(annotation);
147 }
148
149 for (ClassInfo classInfo : classInfos.values()) {
150 if (isMetaRoot(classInfo)) {
151 try {
152 metaroots.add((Class<? extends Annotation>) classInfo.get());
153 } catch (ClassNotFoundException e) {
154 classesNotLoaded.add(classInfo.getName());
155 }
156 }
157 }
158
159 for (Class<? extends Annotation> metaroot : metaroots) {
160 List<Info> infoList = annotated.get(metaroot.getName());
161 for (Info info : infoList) {
162 readClassDef(info.getName() + "$$");
163 }
164 }
165
166 // If the "annotated" list has grown, then we must scan those
167 if (annotated.keySet().size() != annotations.size()) {
168 resolveAnnotations(annotations);
169 }
170 }
171
172 private boolean isMetaRoot(ClassInfo classInfo) {
173 if (!classInfo.isAnnotation()) return false;
174
175 String name = classInfo.getName();
176 if (!name.equals("Metatype") && !name.endsWith(".Metatype") && !name.endsWith("$Metatype")) return false;
177
178 for (AnnotationInfo info : classInfo.getAnnotations()) {
179 if (info.getName().equals(name)) return true;
180 }
181
182 return true;
183 }
184
185 private void linkParent(ClassInfo classInfo) {
186 if (classInfo.superType == null) return;
187 if (classInfo.superType.equals("java.lang.Object")) return;
188
189 ClassInfo parentInfo = classInfo.superclassInfo;
190
191 if (parentInfo == null) {
192
193 parentInfo = classInfos.get(classInfo.superType);
194
195 if (parentInfo == null) {
196
197 if (classInfo.clazz != null) {
198 readClassDef(((Class<?>) classInfo.clazz).getSuperclass());
199 } else {
200 readClassDef(classInfo.superType);
201 }
202
203 parentInfo = classInfos.get(classInfo.superType);
204
205 if (parentInfo == null) return;
206
207 linkParent(parentInfo);
208 }
209
210 classInfo.superclassInfo = parentInfo;
211 }
212
213 if (!parentInfo.subclassInfos.contains(classInfo)) {
214 parentInfo.subclassInfos.add(classInfo);
215 }
216 }
217
218 private void linkInterfaces(ClassInfo classInfo) {
219 final List<ClassInfo> infos = new ArrayList<ClassInfo>();
220
221 if (classInfo.clazz != null) {
222 final Class<?>[] interfaces = classInfo.clazz.getInterfaces();
223
224 for (Class<?> clazz : interfaces) {
225 ClassInfo interfaceInfo = classInfos.get(clazz.getName());
226
227 if (interfaceInfo == null) {
228 readClassDef(clazz);
229 }
230
231 interfaceInfo = classInfos.get(clazz.getName());
232
233 if (interfaceInfo != null) {
234 infos.add(interfaceInfo);
235 }
236 }
237 } else {
238 for (String className : classInfo.interfaces) {
239 ClassInfo interfaceInfo = classInfos.get(className);
240
241 if (interfaceInfo == null) {
242 readClassDef(className);
243 }
244
245 interfaceInfo = classInfos.get(className);
246
247 if (interfaceInfo != null) {
248 infos.add(interfaceInfo);
249 }
250 }
251 }
252
253 for (ClassInfo info : infos) {
254 linkInterfaces(info);
255 }
256 }
257
258 @Override
259 public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
260 List<Info> infos = annotated.get(annotation.getName());
261 return infos != null && !infos.isEmpty();
262 }
263
264 /**
265 * Returns a list of classes that could not be loaded in last invoked findAnnotated* method.
266 * <p/>
267 * The list will only contain entries of classes whose byte code matched the requirements
268 * of last invoked find* method, but were unable to be loaded and included in the results.
269 * <p/>
270 * The list returned is unmodifiable. Once obtained, the returned list will be a live view of the
271 * results from the last findAnnotated* method call.
272 * <p/>
273 * This method is not thread safe.
274 *
275 * @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call.
276 */
277 @Override
278 public List<String> getClassesNotLoaded() {
279 return Collections.unmodifiableList(classesNotLoaded);
280 }
281
282 @Override
283 public List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation) {
284 classesNotLoaded.clear();
285 List<Package> packages = new ArrayList<Package>();
286 List<Info> infos = getAnnotationInfos(annotation.getName());
287 for (Info info : infos) {
288 if (info instanceof PackageInfo) {
289 PackageInfo packageInfo = (PackageInfo) info;
290 try {
291 Package pkg = packageInfo.get();
292 // double check via proper reflection
293 if (pkg.isAnnotationPresent(annotation)) {
294 packages.add(pkg);
295 }
296 } catch (ClassNotFoundException e) {
297 classesNotLoaded.add(packageInfo.getName());
298 }
299 }
300 }
301 return packages;
302 }
303
304 @Override
305 public List<Class<?>> findAnnotatedClasses(Class<? extends Annotation> annotation) {
306 classesNotLoaded.clear();
307 List<Class<?>> classes = new ArrayList<Class<?>>();
308 List<Info> infos = getAnnotationInfos(annotation.getName());
309 for (Info info : infos) {
310 if (info instanceof ClassInfo) {
311 ClassInfo classInfo = (ClassInfo) info;
312 try {
313 Class clazz = classInfo.get();
314 // double check via proper reflection
315 if (clazz.isAnnotationPresent(annotation)) {
316 classes.add(clazz);
317 }
318 } catch (ClassNotFoundException e) {
319 classesNotLoaded.add(classInfo.getName());
320 }
321 }
322 }
323 return classes;
324 }
325
326 @Override
327 public List<Annotated<Class<?>>> findMetaAnnotatedClasses(Class<? extends Annotation> annotation) {
328 classesNotLoaded.clear();
329 Set<Class<?>> classes = findMetaAnnotatedClasses(annotation, new HashSet<Class<?>>());
330
331 List<Annotated<Class<?>>> list = new ArrayList<Annotated<Class<?>>>();
332
333 for (Class<?> clazz : classes) {
334 if (Annotation.class.isAssignableFrom(clazz) && isMetaAnnotation((Class<? extends Annotation>) clazz)) continue;
335 list.add(new MetaAnnotatedClass(clazz));
336 }
337
338 return list;
339 }
340
341 private static boolean isMetaAnnotation(Class<? extends Annotation> clazz) {
342 for (Annotation annotation : clazz.getDeclaredAnnotations()) {
343 if (isMetatypeAnnotation(annotation.annotationType())) return true;
344 }
345
346 return false;
347 }
348
349 private static boolean isMetatypeAnnotation(Class<? extends Annotation> type) {
350 return type.getSimpleName().equals("Metatype") && type.isAnnotationPresent(type);
351 }
352
353
354
355 private Set<Class<?>> findMetaAnnotatedClasses(Class<? extends Annotation> annotation, Set<Class<?>> classes) {
356 List<Info> infos = getAnnotationInfos(annotation.getName());
357 for (Info info : infos) {
358 if (info instanceof ClassInfo) {
359 ClassInfo classInfo = (ClassInfo) info;
360 try {
361 Class clazz = classInfo.get();
362
363 if (classes.contains(clazz)) continue;
364
365 // double check via proper reflection
366 if (clazz.isAnnotationPresent(annotation)) {
367 classes.add(clazz);
368 }
369
370 String meta = info.getMetaAnnotationName();
371 if (meta != null) {
372 classes.addAll(findMetaAnnotatedClasses((Class<? extends Annotation>) clazz, classes));
373 }
374 } catch (ClassNotFoundException e) {
375 classesNotLoaded.add(classInfo.getName());
376 }
377 }
378 }
379 return classes;
380 }
381
382 /**
383 * Naive implementation - works extremelly slow O(n^3)
384 *
385 * @param annotation
386 * @return list of directly or indirectly (inherited) annotated classes
387 */
388 @Override
389 public List<Class<?>> findInheritedAnnotatedClasses(Class<? extends Annotation> annotation) {
390 classesNotLoaded.clear();
391 List<Class<?>> classes = new ArrayList<Class<?>>();
392 List<Info> infos = getAnnotationInfos(annotation.getName());
393 for (Info info : infos) {
394 try {
395 if (info instanceof ClassInfo) {
396 classes.add(((ClassInfo) info).get());
397 }
398 } catch (ClassNotFoundException cnfe) {
399 // TODO: ignored, but a log message would be appropriate
400 }
401 }
402 boolean annClassFound;
403 List<ClassInfo> tempClassInfos = new ArrayList<ClassInfo>(classInfos.values());
404 do {
405 annClassFound = false;
406 for (int pos = 0; pos < tempClassInfos.size(); pos++) {
407 ClassInfo classInfo = tempClassInfos.get(pos);
408 try {
409 // check whether any superclass is annotated
410 String superType = classInfo.getSuperType();
411 for (Class clazz : classes) {
412 if (superType.equals(clazz.getName())) {
413 classes.add(classInfo.get());
414 tempClassInfos.remove(pos);
415 annClassFound = true;
416 break;
417 }
418 }
419 // check whether any interface is annotated
420 List<String> interfces = classInfo.getInterfaces();
421 for (String interfce : interfces) {
422 for (Class clazz : classes) {
423 if (interfce.replaceFirst("<.*>", "").equals(clazz.getName())) {
424 classes.add(classInfo.get());
425 tempClassInfos.remove(pos);
426 annClassFound = true;
427 break;
428 }
429 }
430 }
431 } catch (ClassNotFoundException e) {
432 classesNotLoaded.add(classInfo.getName());
433 } catch (NoClassDefFoundError e) {
434 classesNotLoaded.add(classInfo.getName());
435 }
436 }
437 } while (annClassFound);
438 return classes;
439 }
440
441 @Override
442 public List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation) {
443 classesNotLoaded.clear();
444 List<ClassInfo> seen = new ArrayList<ClassInfo>();
445 List<Method> methods = new ArrayList<Method>();
446 List<Info> infos = getAnnotationInfos(annotation.getName());
447 for (Info info : infos) {
448 if (info instanceof MethodInfo && !info.getName().equals("<init>")) {
449 MethodInfo methodInfo = (MethodInfo) info;
450 ClassInfo classInfo = methodInfo.getDeclaringClass();
451
452 if (seen.contains(classInfo)) continue;
453
454 seen.add(classInfo);
455
456 try {
457 Class clazz = classInfo.get();
458 for (Method method : clazz.getDeclaredMethods()) {
459 if (method.isAnnotationPresent(annotation)) {
460 methods.add(method);
461 }
462 }
463 } catch (ClassNotFoundException e) {
464 classesNotLoaded.add(classInfo.getName());
465 }
466 }
467 }
468 return methods;
469 }
470
471 @Override
472 public List<Annotated<Method>> findMetaAnnotatedMethods(Class<? extends Annotation> annotation) {
473 classesNotLoaded.clear();
474
475 Set<Method> methods = findMetaAnnotatedMethods(annotation, new HashSet<Method>(), new HashSet<String>());
476
477 List<Annotated<Method>> targets = new ArrayList<Annotated<Method>>();
478
479 for (Method method : methods) {
480 targets.add(new MetaAnnotatedMethod(method));
481 }
482
483 return targets;
484 }
485
486 private Set<Method> findMetaAnnotatedMethods(Class<? extends Annotation> annotation, Set<Method> methods, Set<String> seen) {
487 List<Info> infos = getAnnotationInfos(annotation.getName());
488
489 for (Info info : infos) {
490
491 String meta = info.getMetaAnnotationName();
492 if (meta != null) {
493 if (meta.equals(annotation.getName())) continue;
494 if (!seen.add(meta)) continue;
495
496
497 ClassInfo metaInfo = classInfos.get(meta);
498
499 Class<?> clazz;
500 try {
501 clazz = metaInfo.get();
502 } catch (ClassNotFoundException e) {
503 classesNotLoaded.add(metaInfo.getName());
504 continue;
505 }
506
507 findMetaAnnotatedMethods((Class<? extends Annotation>) clazz, methods, seen);
508
509 } else if (info instanceof MethodInfo && !((MethodInfo) info).isConstructor()) {
510
511 MethodInfo methodInfo = (MethodInfo) info;
512
513 ClassInfo classInfo = methodInfo.getDeclaringClass();
514
515 try {
516 Class clazz = classInfo.get();
517 for (Method method : clazz.getDeclaredMethods()) {
518 if (method.isAnnotationPresent(annotation)) {
519 methods.add(method);
520 }
521 }
522 } catch (ClassNotFoundException e) {
523 classesNotLoaded.add(classInfo.getName());
524 }
525 }
526 }
527
528 return methods;
529 }
530
531 @Override
532 public List<Annotated<Field>> findMetaAnnotatedFields(Class<? extends Annotation> annotation) {
533 classesNotLoaded.clear();
534
535 Set<Field> fields = findMetaAnnotatedFields(annotation, new HashSet<Field>(), new HashSet<String>());
536
537 List<Annotated<Field>> targets = new ArrayList<Annotated<Field>>();
538
539 for (Field field : fields) {
540 targets.add(new MetaAnnotatedField(field));
541 }
542
543 return targets;
544 }
545
546 private Set<Field> findMetaAnnotatedFields(Class<? extends Annotation> annotation, Set<Field> fields, Set<String> seen) {
547 List<Info> infos = getAnnotationInfos(annotation.getName());
548
549 for (Info info : infos) {
550
551 String meta = info.getMetaAnnotationName();
552 if (meta != null) {
553 if (meta.equals(annotation.getName())) continue;
554 if (!seen.add(meta)) continue;
555
556
557 ClassInfo metaInfo = classInfos.get(meta);
558
559 Class<?> clazz;
560 try {
561 clazz = metaInfo.get();
562 } catch (ClassNotFoundException e) {
563 classesNotLoaded.add(metaInfo.getName());
564 continue;
565 }
566
567 findMetaAnnotatedFields((Class<? extends Annotation>) clazz, fields, seen);
568
569 } else if (info instanceof FieldInfo) {
570
571 FieldInfo fieldInfo = (FieldInfo) info;
572
573 ClassInfo classInfo = fieldInfo.getDeclaringClass();
574
575 try {
576 Class clazz = classInfo.get();
577 for (Field field : clazz.getDeclaredFields()) {
578 if (field.isAnnotationPresent(annotation)) {
579 fields.add(field);
580 }
581 }
582 } catch (ClassNotFoundException e) {
583 classesNotLoaded.add(classInfo.getName());
584 }
585 }
586 }
587
588 return fields;
589 }
590
591 @Override
592 public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
593 classesNotLoaded.clear();
594 List<ClassInfo> seen = new ArrayList<ClassInfo>();
595 List<Constructor> constructors = new ArrayList<Constructor>();
596 List<Info> infos = getAnnotationInfos(annotation.getName());
597 for (Info info : infos) {
598 if (info instanceof MethodInfo && info.getName().equals("<init>")) {
599 MethodInfo methodInfo = (MethodInfo) info;
600 ClassInfo classInfo = methodInfo.getDeclaringClass();
601
602 if (seen.contains(classInfo)) continue;
603
604 seen.add(classInfo);
605
606 try {
607 Class clazz = classInfo.get();
608 for (Constructor constructor : clazz.getConstructors()) {
609 if (constructor.isAnnotationPresent(annotation)) {
610 constructors.add(constructor);
611 }
612 }
613 } catch (ClassNotFoundException e) {
614 classesNotLoaded.add(classInfo.getName());
615 }
616 }
617 }
618 return constructors;
619 }
620
621 @Override
622 public List<Field> findAnnotatedFields(Class<? extends Annotation> annotation) {
623 classesNotLoaded.clear();
624 List<ClassInfo> seen = new ArrayList<ClassInfo>();
625 List<Field> fields = new ArrayList<Field>();
626 List<Info> infos = getAnnotationInfos(annotation.getName());
627 for (Info info : infos) {
628 if (info instanceof FieldInfo) {
629 FieldInfo fieldInfo = (FieldInfo) info;
630 ClassInfo classInfo = fieldInfo.getDeclaringClass();
631
632 if (seen.contains(classInfo)) continue;
633
634 seen.add(classInfo);
635
636 try {
637 Class clazz = classInfo.get();
638 for (Field field : clazz.getDeclaredFields()) {
639 if (field.isAnnotationPresent(annotation)) {
640 fields.add(field);
641 }
642 }
643 } catch (ClassNotFoundException e) {
644 classesNotLoaded.add(classInfo.getName());
645 }
646 }
647 }
648 return fields;
649 }
650
651 @Override
652 public List<Class<?>> findClassesInPackage(String packageName, boolean recursive) {
653 classesNotLoaded.clear();
654 List<Class<?>> classes = new ArrayList<Class<?>>();
655 for (ClassInfo classInfo : classInfos.values()) {
656 try {
657 if (recursive && classInfo.getPackageName().startsWith(packageName)) {
658 classes.add(classInfo.get());
659 } else if (classInfo.getPackageName().equals(packageName)) {
660 classes.add(classInfo.get());
661 }
662 } catch (ClassNotFoundException e) {
663 classesNotLoaded.add(classInfo.getName());
664 }
665 }
666 return classes;
667 }
668
669 @Override
670 public <T> List<Class<? extends T>> findSubclasses(Class<T> clazz) {
671 if (clazz == null) throw new NullPointerException("class cannot be null");
672
673 classesNotLoaded.clear();
674
675 final ClassInfo classInfo = classInfos.get(clazz.getName());
676
677 List<Class<? extends T>> found = new ArrayList<Class<? extends T>>();
678
679 if (classInfo == null) return found;
680
681 findSubclasses(classInfo, found, clazz);
682
683 return found;
684 }
685
686 private <T> void findSubclasses(ClassInfo classInfo, List<Class<? extends T>> found, Class<T> clazz) {
687
688 for (ClassInfo subclassInfo : classInfo.subclassInfos) {
689
690 try {
691 found.add(subclassInfo.get().asSubclass(clazz));
692 } catch (ClassNotFoundException e) {
693 classesNotLoaded.add(subclassInfo.getName());
694 }
695
696 findSubclasses(subclassInfo, found, clazz);
697 }
698 }
699
700 private <T> List<Class<? extends T>> _findSubclasses(Class<T> clazz) {
701 if (clazz == null) throw new NullPointerException("class cannot be null");
702
703 List<Class<? extends T>> classes = new ArrayList<Class<? extends T>>();
704
705
706 for (ClassInfo classInfo : classInfos.values()) {
707
708 try {
709
710 if (clazz.getName().equals(classInfo.superType)) {
711
712 if (clazz.isAssignableFrom(classInfo.get())) {
713
714 classes.add(classInfo.get().asSubclass(clazz));
715
716 classes.addAll(_findSubclasses(classInfo.get().asSubclass(clazz)));
717 }
718 }
719
720 } catch (ClassNotFoundException e) {
721 classesNotLoaded.add(classInfo.getName());
722 }
723
724 }
725
726 return classes;
727 }
728
729 @Override
730 public <T> List<Class<? extends T>> findImplementations(Class<T> clazz) {
731 if (clazz == null) throw new NullPointerException("class cannot be null");
732 if (!clazz.isInterface()) new IllegalArgumentException("class must be an interface");
733 classesNotLoaded.clear();
734
735 final String interfaceName = clazz.getName();
736
737 // Collect all interfaces extending the main interface (recursively)
738 // Collect all implementations of interfaces
739 // i.e. all *directly* implementing classes
740 List<ClassInfo> infos = collectImplementations(interfaceName);
741
742 // Collect all subclasses of implementations
743 List<Class<? extends T>> classes = new ArrayList<Class<? extends T>>();
744 for (ClassInfo info : infos) {
745 try {
746 final Class<? extends T> impl = (Class<? extends T>) info.get();
747
748 if (clazz.isAssignableFrom(impl)) {
749 classes.add(impl);
750
751 // Optimization: Don't need to call this method if parent class was already searched
752
753
754 classes.addAll(_findSubclasses(impl));
755 }
756
757 } catch (ClassNotFoundException e) {
758 classesNotLoaded.add(info.getName());
759 }
760 }
761 return classes;
762 }
763
764 private List<ClassInfo> collectImplementations(String interfaceName) {
765 final List<ClassInfo> infos = new ArrayList<ClassInfo>();
766
767 for (ClassInfo classInfo : classInfos.values()) {
768
769 if (classInfo.interfaces.contains(interfaceName)) {
770
771 infos.add(classInfo);
772
773 try {
774
775 final Class clazz = classInfo.get();
776
777 if (clazz.isInterface() && !clazz.isAnnotation()) {
778
779 infos.addAll(collectImplementations(classInfo.name));
780
781 }
782
783 } catch (ClassNotFoundException ignore) {
784 // we'll deal with this later
785 }
786 }
787 }
788 return infos;
789 }
790
791 protected List<Info> getAnnotationInfos(String name) {
792 List<Info> infos = annotated.get(name);
793 if (infos == null) {
794 infos = new SingleLinkedList<Info>();
795 annotated.put(name, infos);
796 }
797 return infos;
798 }
799
800 protected void readClassDef(String className) {
801 if (classInfos.containsKey(className)) return;
802 try {
803 readClassDef(archive.getBytecode(className));
804 } catch (Exception e) {
805 if (className.endsWith("$$")) return;
806 classesNotLoaded.add(className);
807 }
808 }
809
810 protected void readClassDef(InputStream in) throws IOException {
811 ClassReader classReader = new ClassReader(in);
812 classReader.accept(new InfoBuildingVisitor(), ASM_FLAGS);
813 }
814
815 protected void readClassDef(Class clazz) {
816 List<Info> infos = new ArrayList<Info>();
817
818 Package aPackage = clazz.getPackage();
819 if (aPackage != null) {
820 final PackageInfo info = new PackageInfo(aPackage);
821 for (AnnotationInfo annotation : info.getAnnotations()) {
822 List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
823 if (!annotationInfos.contains(info)) {
824 annotationInfos.add(info);
825 }
826 }
827 }
828
829 ClassInfo classInfo = new ClassInfo(clazz);
830 infos.add(classInfo);
831 classInfos.put(clazz.getName(), classInfo);
832 for (Method method : clazz.getDeclaredMethods()) {
833 infos.add(new MethodInfo(classInfo, method));
834 }
835
836 for (Constructor constructor : clazz.getConstructors()) {
837 infos.add(new MethodInfo(classInfo, constructor));
838 }
839
840 for (Field field : clazz.getDeclaredFields()) {
841 infos.add(new FieldInfo(classInfo, field));
842 }
843
844 for (Info info : infos) {
845 for (AnnotationInfo annotation : info.getAnnotations()) {
846 List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
847 annotationInfos.add(info);
848 }
849 }
850 }
851
852 public class Annotatable {
853 private final List<AnnotationInfo> annotations = new ArrayList<AnnotationInfo>();
854
855 public Annotatable(AnnotatedElement element) {
856 for (Annotation annotation : getAnnotations(element)) {
857 annotations.add(new AnnotationInfo(annotation.annotationType().getName()));
858 }
859 }
860
861 public Annotatable() {
862 }
863
864 public Annotation[] getDeclaredAnnotations() {
865 return new Annotation[0];
866 }
867
868 public List<AnnotationInfo> getAnnotations() {
869 return annotations;
870 }
871
872 public String getMetaAnnotationName() {
873 return null;
874 }
875
876 /**
877 * Utility method to get around some errors caused by
878 * interactions between the Equinox class loaders and
879 * the OpenJPA transformation process. There is a window
880 * where the OpenJPA transformation process can cause
881 * an annotation being processed to get defined in a
882 * classloader during the actual defineClass call for
883 * that very class (e.g., recursively). This results in
884 * a LinkageError exception. If we see one of these,
885 * retry the request. Since the annotation will be
886 * defined on the second pass, this should succeed. If
887 * we get a second exception, then it's likely some
888 * other problem.
889 *
890 * @param element The AnnotatedElement we need information for.
891 * @return An array of the Annotations defined on the element.
892 */
893 private Annotation[] getAnnotations(AnnotatedElement element) {
894 try {
895 return element.getAnnotations();
896 } catch (LinkageError e) {
897 return element.getAnnotations();
898 }
899 }
900
901 }
902
903 public static interface Info {
904
905 String getMetaAnnotationName();
906
907 String getName();
908
909 List<AnnotationInfo> getAnnotations();
910
911 Annotation[] getDeclaredAnnotations();
912 }
913
914 public class PackageInfo extends Annotatable implements Info {
915 private final String name;
916 private final ClassInfo info;
917 private final Package pkg;
918
919 public PackageInfo(Package pkg) {
920 super(pkg);
921 this.pkg = pkg;
922 this.name = pkg.getName();
923 this.info = null;
924 }
925
926 public PackageInfo(String name) {
927 info = new ClassInfo(name, null);
928 this.name = name;
929 this.pkg = null;
930 }
931
932 public String getName() {
933 return name;
934 }
935
936 public Package get() throws ClassNotFoundException {
937 return (pkg != null) ? pkg : info.get().getPackage();
938 }
939
940 @Override
941 public boolean equals(Object o) {
942 if (this == o) return true;
943 if (o == null || getClass() != o.getClass()) return false;
944
945 PackageInfo that = (PackageInfo) o;
946
947 if (name != null ? !name.equals(that.name) : that.name != null) return false;
948
949 return true;
950 }
951
952 @Override
953 public int hashCode() {
954 return name != null ? name.hashCode() : 0;
955 }
956 }
957
958 public class ClassInfo extends Annotatable implements Info {
959 private String name;
960 private final List<MethodInfo> methods = new SingleLinkedList<MethodInfo>();
961 private final List<MethodInfo> constructors = new SingleLinkedList<MethodInfo>();
962 private String superType;
963 private ClassInfo superclassInfo;
964 private final List<ClassInfo> subclassInfos = new SingleLinkedList<ClassInfo>();
965 private final List<String> interfaces = new SingleLinkedList<String>();
966 private final List<FieldInfo> fields = new SingleLinkedList<FieldInfo>();
967 private Class<?> clazz;
968
969
970 public ClassInfo(Class clazz) {
971 super(clazz);
972 this.clazz = clazz;
973 this.name = clazz.getName();
974 Class superclass = clazz.getSuperclass();
975 this.superType = superclass != null ? superclass.getName() : null;
976 for (Class intrface : clazz.getInterfaces()) {
977 this.interfaces.add(intrface.getName());
978 }
979 }
980
981 public ClassInfo(String name, String superType) {
982 this.name = name;
983 this.superType = superType;
984 }
985
986 @Override
987 public String getMetaAnnotationName() {
988 for (AnnotationInfo info : getAnnotations()) {
989 for (Class<? extends Annotation> metaroot : metaroots) {
990 if (info.getName().equals(metaroot.getName())) return name;
991 }
992 }
993
994 if (name.endsWith("$$")) {
995 ClassInfo info = classInfos.get(name.substring(0, name.length() - 2));
996 if (info != null) {
997 return info.getMetaAnnotationName();
998 }
999 }
1000
1001 return null;
1002 }
1003
1004 public String getPackageName() {
1005 return name.indexOf(".") > 0 ? name.substring(0, name.lastIndexOf(".")) : "";
1006 }
1007
1008 public List<MethodInfo> getConstructors() {
1009 return constructors;
1010 }
1011
1012 public List<String> getInterfaces() {
1013 return interfaces;
1014 }
1015
1016 public List<FieldInfo> getFields() {
1017 return fields;
1018 }
1019
1020 public List<MethodInfo> getMethods() {
1021 return methods;
1022 }
1023
1024 public String getName() {
1025 return name;
1026 }
1027
1028 public String getSuperType() {
1029 return superType;
1030 }
1031
1032 public boolean isAnnotation() {
1033 return "java.lang.Object".equals(superType) && interfaces.size() == 1 && "java.lang.annotation.Annotation".equals(interfaces.get(0));
1034 }
1035
1036 public Class<?> get() throws ClassNotFoundException {
1037 if (clazz != null) return clazz;
1038 try {
1039 String fixedName = name.replaceFirst("<.*>", "");
1040 this.clazz = archive.loadClass(fixedName);
1041 return clazz;
1042 } catch (ClassNotFoundException notFound) {
1043 classesNotLoaded.add(name);
1044 throw notFound;
1045 }
1046 }
1047
1048 public String toString() {
1049 return name;
1050 }
1051 }
1052
1053 public class MethodInfo extends Annotatable implements Info {
1054 private final ClassInfo declaringClass;
1055 private final String descriptor;
1056 private final String name;
1057 private final List<List<AnnotationInfo>> parameterAnnotations = new ArrayList<List<AnnotationInfo>>();
1058 private Member method;
1059
1060 public MethodInfo(ClassInfo info, Constructor constructor) {
1061 super(constructor);
1062 this.declaringClass = info;
1063 this.name = "<init>";
1064 this.descriptor = null;
1065 }
1066
1067 public MethodInfo(ClassInfo info, Method method) {
1068 super(method);
1069 this.declaringClass = info;
1070 this.name = method.getName();
1071 this.descriptor = method.getReturnType().getName();
1072 this.method = method;
1073 }
1074
1075 public MethodInfo(ClassInfo declarignClass, String name, String descriptor) {
1076 this.declaringClass = declarignClass;
1077 this.name = name;
1078 this.descriptor = descriptor;
1079 }
1080
1081 @Override
1082 public String getMetaAnnotationName() {
1083 return declaringClass.getMetaAnnotationName();
1084 }
1085
1086 @Override
1087 public Annotation[] getDeclaredAnnotations() {
1088 super.getDeclaredAnnotations();
1089 try {
1090 return ((AnnotatedElement) get()).getDeclaredAnnotations();
1091 } catch (ClassNotFoundException e) {
1092 return super.getDeclaredAnnotations();
1093 }
1094 }
1095
1096 public boolean isConstructor() {
1097 return getName().equals("<init>");
1098 }
1099
1100 public List<List<AnnotationInfo>> getParameterAnnotations() {
1101 return parameterAnnotations;
1102 }
1103
1104 public List<AnnotationInfo> getParameterAnnotations(int index) {
1105 if (index >= parameterAnnotations.size()) {
1106 for (int i = parameterAnnotations.size(); i <= index; i++) {
1107 List<AnnotationInfo> annotationInfos = new ArrayList<AnnotationInfo>();
1108 parameterAnnotations.add(i, annotationInfos);
1109 }
1110 }
1111 return parameterAnnotations.get(index);
1112 }
1113
1114 public String getName() {
1115 return name;
1116 }
1117
1118 public ClassInfo getDeclaringClass() {
1119 return declaringClass;
1120 }
1121
1122 public String toString() {
1123 return declaringClass + "@" + name;
1124 }
1125
1126 public Member get() throws ClassNotFoundException {
1127 if (method == null) {
1128 method = toMethod();
1129 }
1130
1131 return method;
1132 }
1133
1134 private Method toMethod() throws ClassNotFoundException {
1135 org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method(name, descriptor);
1136
1137 Class<?> clazz = this.declaringClass.get();
1138 List<Class> parameterTypes = new ArrayList<Class>();
1139
1140 for (Type type : method.getArgumentTypes()) {
1141 String paramType = type.getClassName();
1142 try {
1143 parameterTypes.add(Classes.forName(paramType, clazz.getClassLoader()));
1144 } catch (ClassNotFoundException cnfe) {
1145 throw new IllegalStateException("Parameter class could not be loaded for type " + paramType, cnfe);
1146 }
1147 }
1148
1149 Class[] parameters = parameterTypes.toArray(new Class[parameterTypes.size()]);
1150
1151 IllegalStateException noSuchMethod = null;
1152 while (clazz != null) {
1153 try {
1154 return clazz.getDeclaredMethod(name, parameters);
1155 } catch (NoSuchMethodException e) {
1156 if (noSuchMethod == null) {
1157 noSuchMethod = new IllegalStateException("Callback method does not exist: " + clazz.getName() + "." + name, e);
1158 }
1159 clazz = clazz.getSuperclass();
1160 }
1161 }
1162
1163 throw noSuchMethod;
1164 }
1165
1166 }
1167
1168 public class FieldInfo extends Annotatable implements Info {
1169 private final String name;
1170 private final String type;
1171 private final ClassInfo declaringClass;
1172 private Field field;
1173
1174 public FieldInfo(ClassInfo info, Field field) {
1175 super(field);
1176 this.declaringClass = info;
1177 this.name = field.getName();
1178 this.type = field.getType().getName();
1179 this.field = field;
1180 }
1181
1182 public FieldInfo(ClassInfo declaringClass, String name, String type) {
1183 this.declaringClass = declaringClass;
1184 this.name = name;
1185 this.type = type;
1186 }
1187
1188 public String getName() {
1189 return name;
1190 }
1191
1192 public ClassInfo getDeclaringClass() {
1193 return declaringClass;
1194 }
1195
1196 public String getType() {
1197 return type;
1198 }
1199
1200 public String toString() {
1201 return declaringClass + "#" + name;
1202 }
1203
1204 @Override
1205 public String getMetaAnnotationName() {
1206 return declaringClass.getMetaAnnotationName();
1207 }
1208
1209 @Override
1210 public Annotation[] getDeclaredAnnotations() {
1211 super.getDeclaredAnnotations();
1212 try {
1213 return ((AnnotatedElement) get()).getDeclaredAnnotations();
1214 } catch (ClassNotFoundException e) {
1215 return super.getDeclaredAnnotations();
1216 }
1217 }
1218
1219 public Member get() throws ClassNotFoundException {
1220 if (field == null) {
1221 field = toField();
1222 }
1223
1224 return field;
1225 }
1226
1227 private Field toField() throws ClassNotFoundException {
1228
1229 Class<?> clazz = this.declaringClass.get();
1230
1231 try {
1232 return clazz.getDeclaredField(name);
1233 } catch (NoSuchFieldException e) {
1234 throw new IllegalStateException(name, e);
1235 }
1236
1237 }
1238 }
1239
1240 public class AnnotationInfo extends Annotatable implements Info {
1241 private final String name;
1242
1243 public AnnotationInfo(Annotation annotation) {
1244 this(annotation.getClass().getName());
1245 }
1246
1247 public AnnotationInfo(Class<? extends Annotation> annotation) {
1248 this.name = annotation.getName().intern();
1249 }
1250
1251 public AnnotationInfo(String name) {
1252 name = Type.getType(name).getClassName();
1253 this.name = name.intern();
1254 }
1255
1256 public String getName() {
1257 return name;
1258 }
1259
1260 public String toString() {
1261 return name;
1262 }
1263 }
1264
1265 public class InfoBuildingVisitor extends EmptyVisitor {
1266 private Info info;
1267
1268 public InfoBuildingVisitor() {
1269 }
1270
1271 public InfoBuildingVisitor(Info info) {
1272 this.info = info;
1273 }
1274
1275 @Override
1276 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
1277 if (name.endsWith("package-info")) {
1278 info = new PackageInfo(javaName(name));
1279 } else {
1280
1281 ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName));
1282
1283 // if (signature == null) {
1284 for (String interfce : interfaces) {
1285 classInfo.getInterfaces().add(javaName(interfce));
1286 }
1287 // } else {
1288 // // the class uses generics
1289 // new SignatureReader(signature).accept(new GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE.CLASS, classInfo));
1290 // }
1291 info = classInfo;
1292
1293 classInfos.put(classInfo.getName(), classInfo);
1294 }
1295 }
1296
1297 private String javaName(String name) {
1298 return (name == null) ? null : name.replace('/', '.');
1299 }
1300
1301 @Override
1302 public void visitInnerClass(String name, String outerName, String innerName, int access) {
1303 super.visitInnerClass(name, outerName, innerName, access);
1304 }
1305
1306 @Override
1307 public void visitAttribute(Attribute attribute) {
1308 super.visitAttribute(attribute);
1309 }
1310
1311 @Override
1312 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
1313 AnnotationInfo annotationInfo = new AnnotationInfo(desc);
1314 info.getAnnotations().add(annotationInfo);
1315 getAnnotationInfos(annotationInfo.getName()).add(info);
1316 return new InfoBuildingVisitor(annotationInfo);
1317 }
1318
1319 @Override
1320 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
1321 ClassInfo classInfo = ((ClassInfo) info);
1322 FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc);
1323 classInfo.getFields().add(fieldInfo);
1324 return new InfoBuildingVisitor(fieldInfo);
1325 }
1326
1327 @Override
1328 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
1329 ClassInfo classInfo = ((ClassInfo) info);
1330 MethodInfo methodInfo = new MethodInfo(classInfo, name, desc);
1331
1332 classInfo.getMethods().add(methodInfo);
1333 return new InfoBuildingVisitor(methodInfo);
1334 }
1335
1336
1337 @Override
1338 public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) {
1339 MethodInfo methodInfo = ((MethodInfo) info);
1340 List<AnnotationInfo> annotationInfos = methodInfo.getParameterAnnotations(param);
1341 AnnotationInfo annotationInfo = new AnnotationInfo(desc);
1342 annotationInfos.add(annotationInfo);
1343 return new InfoBuildingVisitor(annotationInfo);
1344 }
1345 }
1346
1347 public static class GenericAwareInfoBuildingVisitor implements SignatureVisitor {
1348
1349 public enum TYPE {
1350 CLASS
1351 }
1352
1353 public enum STATE {
1354 BEGIN, END, SUPERCLASS, INTERFACE, FORMAL_TYPE_PARAM
1355 }
1356
1357 private Info info;
1358 private GenericAwareInfoBuildingVisitor.TYPE type;
1359 private GenericAwareInfoBuildingVisitor.STATE state;
1360
1361 private static boolean debug = false;
1362
1363 public GenericAwareInfoBuildingVisitor() {
1364 }
1365
1366 public GenericAwareInfoBuildingVisitor(GenericAwareInfoBuildingVisitor.TYPE type, Info info) {
1367 this.type = type;
1368 this.info = info;
1369 this.state = GenericAwareInfoBuildingVisitor.STATE.BEGIN;
1370 }
1371
1372 public void visitFormalTypeParameter(String s) {
1373 if (debug) System.out.println(" s=" + s);
1374 switch (state) {
1375 case BEGIN:
1376 ((ClassInfo) info).name += "<" + s;
1377 }
1378 state = GenericAwareInfoBuildingVisitor.STATE.FORMAL_TYPE_PARAM;
1379 }
1380
1381 public SignatureVisitor visitClassBound() {
1382 if (debug) System.out.println(" visitClassBound()");
1383 return this;
1384 }
1385
1386 public SignatureVisitor visitInterfaceBound() {
1387 if (debug) System.out.println(" visitInterfaceBound()");
1388 return this;
1389 }
1390
1391 public SignatureVisitor visitSuperclass() {
1392 if (debug) System.out.println(" visitSuperclass()");
1393 state = GenericAwareInfoBuildingVisitor.STATE.SUPERCLASS;
1394 return this;
1395 }
1396
1397 public SignatureVisitor visitInterface() {
1398 if (debug) System.out.println(" visitInterface()");
1399 ((ClassInfo) info).getInterfaces().add("");
1400 state = GenericAwareInfoBuildingVisitor.STATE.INTERFACE;
1401 return this;
1402 }
1403
1404 public SignatureVisitor visitParameterType() {
1405 if (debug) System.out.println(" visitParameterType()");
1406 return this;
1407 }
1408
1409 public SignatureVisitor visitReturnType() {
1410 if (debug) System.out.println(" visitReturnType()");
1411 return this;
1412 }
1413
1414 public SignatureVisitor visitExceptionType() {
1415 if (debug) System.out.println(" visitExceptionType()");
1416 return this;
1417 }
1418
1419 public void visitBaseType(char c) {
1420 if (debug) System.out.println(" visitBaseType(" + c + ")");
1421 }
1422
1423 public void visitTypeVariable(String s) {
1424 if (debug) System.out.println(" visitTypeVariable(" + s + ")");
1425 }
1426
1427 public SignatureVisitor visitArrayType() {
1428 if (debug) System.out.println(" visitArrayType()");
1429 return this;
1430 }
1431
1432 public void visitClassType(String s) {
1433 if (debug) System.out.println(" visitClassType(" + s + ")");
1434 switch (state) {
1435 case INTERFACE:
1436 List<String> interfces = ((ClassInfo) info).getInterfaces();
1437 int idx = interfces.size() - 1;
1438 String interfce = interfces.get(idx);
1439 if (interfce.length() == 0) {
1440 interfce = javaName(s);
1441 } else {
1442 interfce += javaName(s);
1443 }
1444 interfces.set(idx, interfce);
1445 break;
1446 case SUPERCLASS:
1447 if (!s.equals("java/lang/Object")) {
1448 ((ClassInfo) info).superType = javaName(s);
1449 }
1450 }
1451 }
1452
1453 public void visitInnerClassType(String s) {
1454 if (debug) System.out.println(" visitInnerClassType(" + s + ")");
1455 }
1456
1457 public void visitTypeArgument() {
1458 if (debug) System.out.println(" visitTypeArgument()");
1459 switch (state) {
1460 case INTERFACE:
1461 List<String> interfces = ((ClassInfo) info).getInterfaces();
1462 int idx = interfces.size() - 1;
1463 String interfce = interfces.get(idx);
1464 interfce += "<";
1465 interfces.set(idx, interfce);
1466 }
1467 }
1468
1469 public SignatureVisitor visitTypeArgument(char c) {
1470 if (debug) System.out.println(" visitTypeArgument(" + c + ")");
1471 switch (state) {
1472 case INTERFACE:
1473 List<String> interfces = ((ClassInfo) info).getInterfaces();
1474 int idx = interfces.size() - 1;
1475 String interfce = interfces.get(idx);
1476 interfce += "<";
1477 interfces.set(idx, interfce);
1478 }
1479 return this;
1480 }
1481
1482 public void visitEnd() {
1483 if (debug) System.out.println(" visitEnd()");
1484 switch (state) {
1485 case INTERFACE:
1486 List<String> interfces = ((ClassInfo) info).getInterfaces();
1487 int idx = interfces.size() - 1;
1488 String interfce = interfces.get(idx);
1489 interfce += ">";
1490 interfces.set(idx, interfce);
1491 break;
1492 case FORMAL_TYPE_PARAM:
1493 String name = ((ClassInfo) info).name;
1494 if (name.contains("<")) {
1495 ((ClassInfo) info).name += ">";
1496 }
1497 }
1498 state = GenericAwareInfoBuildingVisitor.STATE.END;
1499 }
1500
1501 private String javaName(String name) {
1502 return (name == null) ? null : name.replace('/', '.');
1503 }
1504
1505 }
1506
1507 }