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