001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.finder;
018
019 import org.objectweb.asm.AnnotationVisitor;
020 import org.objectweb.asm.ClassReader;
021 import org.objectweb.asm.FieldVisitor;
022 import org.objectweb.asm.MethodVisitor;
023 import org.objectweb.asm.commons.EmptyVisitor;
024
025 import java.io.File;
026 import java.io.IOException;
027 import java.io.InputStream;
028 import java.lang.annotation.Annotation;
029 import java.lang.reflect.Constructor;
030 import java.lang.reflect.Field;
031 import java.lang.reflect.Method;
032 import java.lang.reflect.AnnotatedElement;
033 import java.net.URL;
034 import java.net.JarURLConnection;
035 import java.net.URLDecoder;
036 import java.util.ArrayList;
037 import java.util.Arrays;
038 import java.util.Collection;
039 import java.util.Collections;
040 import java.util.Enumeration;
041 import java.util.HashMap;
042 import java.util.List;
043 import java.util.Map;
044 import java.util.jar.JarEntry;
045 import java.util.jar.JarInputStream;
046
047 /**
048 * ClassFinder searches the classpath of the specified classloader for
049 * packages, classes, constructors, methods, or fields with specific annotations.
050 *
051 * For security reasons ASM is used to find the annotations. Classes are not
052 * loaded unless they match the requirements of a called findAnnotated* method.
053 * Once loaded, these classes are cached.
054 *
055 * The getClassesNotLoaded() method can be used immediately after any find*
056 * method to get a list of classes which matched the find requirements (i.e.
057 * contained the annotation), but were unable to be loaded.
058 *
059 * @author David Blevins
060 * @version $Rev: 731945 $ $Date: 2009-01-06 09:19:28 -0500 (Tue, 06 Jan 2009) $
061 */
062 public class ClassFinder {
063 private final Map<String, List<Info>> annotated = new HashMap<String, List<Info>>();
064 private final List<ClassInfo> classInfos = new ArrayList<ClassInfo>();
065
066 private final ClassLoader classLoader;
067 private final List<String> classesNotLoaded = new ArrayList<String>();
068 private final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES;
069
070 /**
071 * Creates a ClassFinder that will search the urls in the specified classloader
072 * excluding the urls in the classloader's parent.
073 *
074 * To include the parent classloader, use:
075 *
076 * new ClassFinder(classLoader, false);
077 *
078 * To exclude the parent's parent, use:
079 *
080 * new ClassFinder(classLoader, classLoader.getParent().getParent());
081 *
082 * @param classLoader source of classes to scan
083 * @throws Exception if something goes wrong
084 */
085 public ClassFinder(ClassLoader classLoader) throws Exception {
086 this(classLoader, true);
087 }
088
089 /**
090 * Creates a ClassFinder that will search the urls in the specified classloader.
091 *
092 * @param classLoader source of classes to scan
093 * @param excludeParent Allegedly excludes classes from parent classloader, whatever that might mean
094 * @throws Exception if something goes wrong.
095 */
096 public ClassFinder(ClassLoader classLoader, boolean excludeParent) throws Exception {
097 this(classLoader, getUrls(classLoader, excludeParent));
098 }
099
100 /**
101 * Creates a ClassFinder that will search the urls in the specified classloader excluding
102 * the urls in the 'exclude' classloader.
103 *
104 * @param classLoader source of classes to scan
105 * @param exclude source of classes to exclude from scanning
106 * @throws Exception if something goes wrong
107 */
108 public ClassFinder(ClassLoader classLoader, ClassLoader exclude) throws Exception {
109 this(classLoader, getUrls(classLoader, exclude));
110 }
111
112 public ClassFinder(ClassLoader classLoader, URL url) {
113 this(classLoader, Arrays.asList(url));
114 }
115
116 public ClassFinder(ClassLoader classLoader, Collection<URL> urls) {
117 this.classLoader = classLoader;
118
119 List<String> classNames = new ArrayList<String>();
120 for (URL location : urls) {
121 try {
122 if (location.getProtocol().equals("jar")) {
123 classNames.addAll(jar(location));
124 } else if (location.getProtocol().equals("file")) {
125 try {
126 // See if it's actually a jar
127 URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/");
128 JarURLConnection juc = (JarURLConnection) jarUrl.openConnection();
129 juc.getJarFile();
130 classNames.addAll(jar(jarUrl));
131 } catch (IOException e) {
132 classNames.addAll(file(location));
133 }
134 }
135 } catch (Exception e) {
136 e.printStackTrace();
137 }
138 }
139
140 for (String className : classNames) {
141 readClassDef(className);
142 }
143 }
144
145 public ClassFinder(Class... classes){
146 this(Arrays.asList(classes));
147 }
148
149 public ClassFinder(List<Class> classes){
150 this.classLoader = null;
151 List<Info> infos = new ArrayList<Info>();
152 List<Package> packages = new ArrayList<Package>();
153 for (Class clazz : classes) {
154
155 Package aPackage = clazz.getPackage();
156 if (aPackage != null && !packages.contains(aPackage)){
157 infos.add(new PackageInfo(aPackage));
158 packages.add(aPackage);
159 }
160
161 ClassInfo classInfo = new ClassInfo(clazz);
162 infos.add(classInfo);
163 classInfos.add(classInfo);
164 for (Method method : clazz.getDeclaredMethods()) {
165 infos.add(new MethodInfo(classInfo, method));
166 }
167
168 for (Constructor constructor : clazz.getConstructors()) {
169 infos.add(new MethodInfo(classInfo, constructor));
170 }
171
172 for (Field field : clazz.getDeclaredFields()) {
173 infos.add(new FieldInfo(classInfo, field));
174 }
175 }
176
177 for (Info info : infos) {
178 for (AnnotationInfo annotation : info.getAnnotations()) {
179 List<Info> annotationInfos = getAnnotationInfos(annotation.getName());
180 annotationInfos.add(info);
181 }
182 }
183 }
184
185 public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
186 List<Info> infos = annotated.get(annotation.getName());
187 return infos != null && !infos.isEmpty();
188 }
189
190 /**
191 * Returns a list of classes that could not be loaded in last invoked findAnnotated* method.
192 * <p/>
193 * The list will only contain entries of classes whose byte code matched the requirements
194 * of last invoked find* method, but were unable to be loaded and included in the results.
195 * <p/>
196 * The list returned is unmodifiable. Once obtained, the returned list will be a live view of the
197 * results from the last findAnnotated* method call.
198 * <p/>
199 * This method is not thread safe.
200 * @return an unmodifiable live view of classes that could not be loaded in previous findAnnotated* call.
201 */
202 public List<String> getClassesNotLoaded() {
203 return Collections.unmodifiableList(classesNotLoaded);
204 }
205
206 public List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation) {
207 classesNotLoaded.clear();
208 List<Package> packages = new ArrayList<Package>();
209 List<Info> infos = getAnnotationInfos(annotation.getName());
210 for (Info info : infos) {
211 if (info instanceof PackageInfo) {
212 PackageInfo packageInfo = (PackageInfo) info;
213 try {
214 Package pkg = packageInfo.get();
215 // double check via proper reflection
216 if (pkg.isAnnotationPresent(annotation)) {
217 packages.add(pkg);
218 }
219 } catch (ClassNotFoundException e) {
220 classesNotLoaded.add(packageInfo.getName());
221 }
222 }
223 }
224 return packages;
225 }
226
227 public List<Class> findAnnotatedClasses(Class<? extends Annotation> annotation) {
228 classesNotLoaded.clear();
229 List<Class> classes = new ArrayList<Class>();
230 List<Info> infos = getAnnotationInfos(annotation.getName());
231 for (Info info : infos) {
232 if (info instanceof ClassInfo) {
233 ClassInfo classInfo = (ClassInfo) info;
234 try {
235 Class clazz = classInfo.get();
236 // double check via proper reflection
237 if (clazz.isAnnotationPresent(annotation)) {
238 classes.add(clazz);
239 }
240 } catch (ClassNotFoundException e) {
241 classesNotLoaded.add(classInfo.getName());
242 }
243 }
244 }
245 return classes;
246 }
247
248 /**
249 * Naive implementation - works extremelly slow O(n^3)
250 *
251 * @param annotation
252 * @return list of directly or indirectly (inherited) annotated classes
253 */
254 public List<Class> findInheritedAnnotatedClasses(Class<? extends Annotation> annotation) {
255 classesNotLoaded.clear();
256 List<Class> classes = new ArrayList<Class>();
257 List<Info> infos = getAnnotationInfos(annotation.getName());
258 for (Info info : infos) {
259 try {
260 classes.add(((ClassInfo) info).get());
261 } catch (ClassNotFoundException cnfe) {
262 // TODO: ignored, but a log message would be appropriate
263 }
264 }
265 boolean annClassFound;
266 List<ClassInfo> tempClassInfos = new ArrayList<ClassInfo>(classInfos);
267 do {
268 annClassFound = false;
269 for (int pos = 0; pos < tempClassInfos.size(); pos++) {
270 ClassInfo classInfo = tempClassInfos.get(pos);
271 try {
272 String superType = classInfo.getSuperType();
273 for (Class clazz : classes) {
274 if (superType.equals(clazz.getName())) {
275 classes.add(classInfo.get());
276 tempClassInfos.remove(pos);
277 annClassFound = true;
278 break;
279 }
280 }
281 } catch (ClassNotFoundException e) {
282 classesNotLoaded.add(classInfo.getName());
283 } catch (NoClassDefFoundError e) {
284 classesNotLoaded.add(classInfo.getName());
285 }
286 }
287 } while (annClassFound);
288 return classes;
289 }
290
291 public List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation) {
292 classesNotLoaded.clear();
293 List<ClassInfo> seen = new ArrayList<ClassInfo>();
294 List<Method> methods = new ArrayList<Method>();
295 List<Info> infos = getAnnotationInfos(annotation.getName());
296 for (Info info : infos) {
297 if (info instanceof MethodInfo && !info.getName().equals("<init>")) {
298 MethodInfo methodInfo = (MethodInfo) info;
299 ClassInfo classInfo = methodInfo.getDeclaringClass();
300
301 if (seen.contains(classInfo)) continue;
302
303 seen.add(classInfo);
304
305 try {
306 Class clazz = classInfo.get();
307 for (Method method : clazz.getDeclaredMethods()) {
308 if (method.isAnnotationPresent(annotation)) {
309 methods.add(method);
310 }
311 }
312 } catch (ClassNotFoundException e) {
313 classesNotLoaded.add(classInfo.getName());
314 }
315 }
316 }
317 return methods;
318 }
319
320 public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
321 classesNotLoaded.clear();
322 List<ClassInfo> seen = new ArrayList<ClassInfo>();
323 List<Constructor> constructors = new ArrayList<Constructor>();
324 List<Info> infos = getAnnotationInfos(annotation.getName());
325 for (Info info : infos) {
326 if (info instanceof MethodInfo && info.getName().equals("<init>")) {
327 MethodInfo methodInfo = (MethodInfo) info;
328 ClassInfo classInfo = methodInfo.getDeclaringClass();
329
330 if (seen.contains(classInfo)) continue;
331
332 seen.add(classInfo);
333
334 try {
335 Class clazz = classInfo.get();
336 for (Constructor constructor : clazz.getConstructors()) {
337 if (constructor.isAnnotationPresent(annotation)) {
338 constructors.add(constructor);
339 }
340 }
341 } catch (ClassNotFoundException e) {
342 classesNotLoaded.add(classInfo.getName());
343 }
344 }
345 }
346 return constructors;
347 }
348
349 public List<Field> findAnnotatedFields(Class<? extends Annotation> annotation) {
350 classesNotLoaded.clear();
351 List<ClassInfo> seen = new ArrayList<ClassInfo>();
352 List<Field> fields = new ArrayList<Field>();
353 List<Info> infos = getAnnotationInfos(annotation.getName());
354 for (Info info : infos) {
355 if (info instanceof FieldInfo) {
356 FieldInfo fieldInfo = (FieldInfo) info;
357 ClassInfo classInfo = fieldInfo.getDeclaringClass();
358
359 if (seen.contains(classInfo)) continue;
360
361 seen.add(classInfo);
362
363 try {
364 Class clazz = classInfo.get();
365 for (Field field : clazz.getDeclaredFields()) {
366 if (field.isAnnotationPresent(annotation)) {
367 fields.add(field);
368 }
369 }
370 } catch (ClassNotFoundException e) {
371 classesNotLoaded.add(classInfo.getName());
372 }
373 }
374 }
375 return fields;
376 }
377
378 public List<Class> findClassesInPackage(String packageName, boolean recursive) {
379 classesNotLoaded.clear();
380 List<Class> classes = new ArrayList<Class>();
381 for (ClassInfo classInfo : classInfos) {
382 try {
383 if (recursive && classInfo.getPackageName().startsWith(packageName)){
384 classes.add(classInfo.get());
385 } else if (classInfo.getPackageName().equals(packageName)){
386 classes.add(classInfo.get());
387 }
388 } catch (ClassNotFoundException e) {
389 classesNotLoaded.add(classInfo.getName());
390 }
391 }
392 return classes;
393 }
394
395 private static Collection<URL> getUrls(ClassLoader classLoader, boolean excludeParent) throws IOException {
396 return getUrls(classLoader, excludeParent? classLoader.getParent() : null);
397 }
398
399 private static Collection<URL> getUrls(ClassLoader classLoader, ClassLoader excludeParent) throws IOException {
400 UrlSet urlSet = new UrlSet(classLoader);
401 if (excludeParent != null){
402 urlSet = urlSet.exclude(excludeParent);
403 }
404 return urlSet.getUrls();
405 }
406
407 private List<String> file(URL location) {
408 List<String> classNames = new ArrayList<String>();
409 File dir = new File(URLDecoder.decode(location.getPath()));
410 if (dir.getName().equals("META-INF")) {
411 dir = dir.getParentFile(); // Scrape "META-INF" off
412 }
413 if (dir.isDirectory()) {
414 scanDir(dir, classNames, "");
415 }
416 return classNames;
417 }
418
419 private void scanDir(File dir, List<String> classNames, String packageName) {
420 File[] files = dir.listFiles();
421 for (File file : files) {
422 if (file.isDirectory()) {
423 scanDir(file, classNames, packageName + file.getName() + ".");
424 } else if (file.getName().endsWith(".class")) {
425 String name = file.getName();
426 name = name.replaceFirst(".class$", "");
427 classNames.add(packageName + name);
428 }
429 }
430 }
431
432 private List<String> jar(URL location) throws IOException {
433 String jarPath = location.getFile();
434 if (jarPath.indexOf("!") > -1){
435 jarPath = jarPath.substring(0, jarPath.indexOf("!"));
436 }
437 URL url = new URL(jarPath);
438 InputStream in = url.openStream();
439 try {
440 JarInputStream jarStream = new JarInputStream(in);
441 return jar(jarStream);
442 } finally {
443 in.close();
444 }
445 }
446
447 private List<String> jar(JarInputStream jarStream) throws IOException {
448 List<String> classNames = new ArrayList<String>();
449
450 JarEntry entry;
451 while ((entry = jarStream.getNextJarEntry()) != null) {
452 if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
453 continue;
454 }
455 String className = entry.getName();
456 className = className.replaceFirst(".class$", "");
457 className = className.replace('/', '.');
458 classNames.add(className);
459 }
460
461 return classNames;
462 }
463
464 public class Annotatable {
465 private final List<AnnotationInfo> annotations = new ArrayList<AnnotationInfo>();
466
467 public Annotatable(AnnotatedElement element) {
468 for (Annotation annotation : element.getAnnotations()) {
469 annotations.add(new AnnotationInfo(annotation.annotationType().getName()));
470 }
471 }
472
473 public Annotatable() {
474 }
475
476 public List<AnnotationInfo> getAnnotations() {
477 return annotations;
478 }
479
480 }
481
482 public static interface Info {
483 String getName();
484
485 List<AnnotationInfo> getAnnotations();
486 }
487
488 public class PackageInfo extends Annotatable implements Info {
489 private final String name;
490 private final ClassInfo info;
491 private final Package pkg;
492
493 public PackageInfo(Package pkg){
494 super(pkg);
495 this.pkg = pkg;
496 this.name = pkg.getName();
497 this.info = null;
498 }
499
500 public PackageInfo(String name) {
501 info = new ClassInfo(name, null);
502 this.name = name;
503 this.pkg = null;
504 }
505
506 public String getName() {
507 return name;
508 }
509
510 public Package get() throws ClassNotFoundException {
511 return (pkg != null)?pkg:info.get().getPackage();
512 }
513 }
514
515 public class ClassInfo extends Annotatable implements Info {
516 private final String name;
517 private final List<MethodInfo> methods = new ArrayList<MethodInfo>();
518 private final List<MethodInfo> constructors = new ArrayList<MethodInfo>();
519 private final String superType;
520 private final List<String> interfaces = new ArrayList<String>();
521 private final List<FieldInfo> fields = new ArrayList<FieldInfo>();
522 private Class<?> clazz;
523 private ClassNotFoundException notFound;
524
525 public ClassInfo(Class clazz) {
526 super(clazz);
527 this.clazz = clazz;
528 this.name = clazz.getName();
529 Class superclass = clazz.getSuperclass();
530 this.superType = superclass != null ? superclass.getName(): null;
531 }
532
533 public ClassInfo(String name, String superType) {
534 this.name = name;
535 this.superType = superType;
536 }
537
538 public String getPackageName(){
539 return name.substring(name.lastIndexOf(".")+1, name.length());
540 }
541
542 public List<MethodInfo> getConstructors() {
543 return constructors;
544 }
545
546 public List<String> getInterfaces() {
547 return interfaces;
548 }
549
550 public List<FieldInfo> getFields() {
551 return fields;
552 }
553
554 public List<MethodInfo> getMethods() {
555 return methods;
556 }
557
558 public String getName() {
559 return name;
560 }
561
562 public String getSuperType() {
563 return superType;
564 }
565
566 public Class get() throws ClassNotFoundException {
567 if (clazz != null) return clazz;
568 if (notFound != null) throw notFound;
569 try {
570 this.clazz = classLoader.loadClass(name);
571 return clazz;
572 } catch (ClassNotFoundException notFound) {
573 classesNotLoaded.add(name);
574 this.notFound = notFound;
575 throw notFound;
576 }
577 }
578
579 public String toString() {
580 return name;
581 }
582 }
583
584 public class MethodInfo extends Annotatable implements Info {
585 private final ClassInfo declaringClass;
586 private final String returnType;
587 private final String name;
588 private final List<List<AnnotationInfo>> parameterAnnotations = new ArrayList<List<AnnotationInfo>>();
589
590 public MethodInfo(ClassInfo info, Constructor constructor){
591 super(constructor);
592 this.declaringClass = info;
593 this.name = "<init>";
594 this.returnType = Void.TYPE.getName();
595 }
596
597 public MethodInfo(ClassInfo info, Method method){
598 super(method);
599 this.declaringClass = info;
600 this.name = method.getName();
601 this.returnType = method.getReturnType().getName();
602 }
603
604 public MethodInfo(ClassInfo declarignClass, String name, String returnType) {
605 this.declaringClass = declarignClass;
606 this.name = name;
607 this.returnType = returnType;
608 }
609
610 public List<List<AnnotationInfo>> getParameterAnnotations() {
611 return parameterAnnotations;
612 }
613
614 public List<AnnotationInfo> getParameterAnnotations(int index) {
615 if (index >= parameterAnnotations.size()) {
616 for (int i = parameterAnnotations.size(); i <= index; i++) {
617 List<AnnotationInfo> annotationInfos = new ArrayList<AnnotationInfo>();
618 parameterAnnotations.add(i, annotationInfos);
619 }
620 }
621 return parameterAnnotations.get(index);
622 }
623
624 public String getName() {
625 return name;
626 }
627
628 public ClassInfo getDeclaringClass() {
629 return declaringClass;
630 }
631
632 public String getReturnType() {
633 return returnType;
634 }
635
636 public String toString() {
637 return declaringClass + "@" + name;
638 }
639 }
640
641 public class FieldInfo extends Annotatable implements Info {
642 private final String name;
643 private final String type;
644 private final ClassInfo declaringClass;
645
646 public FieldInfo(ClassInfo info, Field field){
647 super(field);
648 this.declaringClass = info;
649 this.name = field.getName();
650 this.type = field.getType().getName();
651 }
652
653 public FieldInfo(ClassInfo declaringClass, String name, String type) {
654 this.declaringClass = declaringClass;
655 this.name = name;
656 this.type = type;
657 }
658
659 public String getName() {
660 return name;
661 }
662
663 public ClassInfo getDeclaringClass() {
664 return declaringClass;
665 }
666
667 public String getType() {
668 return type;
669 }
670
671 public String toString() {
672 return declaringClass + "#" + name;
673 }
674 }
675
676 public class AnnotationInfo extends Annotatable implements Info {
677 private final String name;
678
679 public AnnotationInfo(Annotation annotation){
680 this(annotation.getClass().getName());
681 }
682
683 public AnnotationInfo(Class<? extends Annotation> annotation) {
684 this.name = annotation.getName().intern();
685 }
686
687 public AnnotationInfo(String name) {
688 name = name.replaceAll("^L|;$", "");
689 name = name.replace('/', '.');
690 this.name = name.intern();
691 }
692
693 public String getName() {
694 return name;
695 }
696
697 public String toString() {
698 return name;
699 }
700 }
701
702 private List<Info> getAnnotationInfos(String name) {
703 List<Info> infos = annotated.get(name);
704 if (infos == null) {
705 infos = new ArrayList<Info>();
706 annotated.put(name, infos);
707 }
708 return infos;
709 }
710
711 private void readClassDef(String className) {
712 if (!className.endsWith(".class")) {
713 className = className.replace('.', '/') + ".class";
714 }
715 try {
716 URL resource = classLoader.getResource(className);
717 if (resource != null) {
718 InputStream in = resource.openStream();
719 try {
720 ClassReader classReader = new ClassReader(in);
721 classReader.accept(new InfoBuildingVisitor(), ASM_FLAGS);
722 } finally {
723 in.close();
724 }
725 } else {
726 new Exception("Could not load " + className).printStackTrace();
727 }
728 } catch (IOException e) {
729 e.printStackTrace();
730 }
731
732 }
733
734 public class InfoBuildingVisitor extends EmptyVisitor {
735 private Info info;
736
737 public InfoBuildingVisitor() {
738 }
739
740 public InfoBuildingVisitor(Info info) {
741 this.info = info;
742 }
743
744 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
745 if (name.endsWith("package-info")) {
746 info = new PackageInfo(javaName(name));
747 } else {
748 ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName));
749
750 for (String interfce : interfaces) {
751 classInfo.getInterfaces().add(javaName(interfce));
752 }
753 info = classInfo;
754 classInfos.add(classInfo);
755 }
756 }
757
758 private String javaName(String name) {
759 return (name == null)? null:name.replace('/', '.');
760 }
761
762 public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
763 AnnotationInfo annotationInfo = new AnnotationInfo(desc);
764 info.getAnnotations().add(annotationInfo);
765 getAnnotationInfos(annotationInfo.getName()).add(info);
766 return new InfoBuildingVisitor(annotationInfo);
767 }
768
769 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
770 ClassInfo classInfo = ((ClassInfo) info);
771 FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc);
772 classInfo.getFields().add(fieldInfo);
773 return new InfoBuildingVisitor(fieldInfo);
774 }
775
776 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
777 ClassInfo classInfo = ((ClassInfo) info);
778 MethodInfo methodInfo = new MethodInfo(classInfo, name, desc);
779 classInfo.getMethods().add(methodInfo);
780 return new InfoBuildingVisitor(methodInfo);
781 }
782
783 public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) {
784 MethodInfo methodInfo = ((MethodInfo) info);
785 List<AnnotationInfo> annotationInfos = methodInfo.getParameterAnnotations(param);
786 AnnotationInfo annotationInfo = new AnnotationInfo(desc);
787 annotationInfos.add(annotationInfo);
788 return new InfoBuildingVisitor(annotationInfo);
789 }
790 }
791 }