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