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    }