1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.tools.layoutlib.create;
18
19import org.objectweb.asm.AnnotationVisitor;
20import org.objectweb.asm.Attribute;
21import org.objectweb.asm.ClassReader;
22import org.objectweb.asm.ClassVisitor;
23import org.objectweb.asm.FieldVisitor;
24import org.objectweb.asm.Label;
25import org.objectweb.asm.MethodVisitor;
26import org.objectweb.asm.Opcodes;
27import org.objectweb.asm.Type;
28import org.objectweb.asm.signature.SignatureReader;
29import org.objectweb.asm.signature.SignatureVisitor;
30
31import java.io.IOException;
32import java.util.ArrayList;
33import java.util.Enumeration;
34import java.util.List;
35import java.util.Map;
36import java.util.Map.Entry;
37import java.util.TreeMap;
38import java.util.regex.Pattern;
39import java.util.zip.ZipEntry;
40import java.util.zip.ZipFile;
41
42/**
43 * Analyzes the input JAR using the ASM java bytecode manipulation library
44 * to list the desired classes and their dependencies.
45 */
46public class AsmAnalyzer {
47
48    // Note: a bunch of stuff has package-level access for unit tests. Consider it private.
49
50    /** Output logger. */
51    private final Log mLog;
52    /** The input source JAR to parse. */
53    private final List<String> mOsSourceJar;
54    /** The generator to fill with the class list and dependency list. */
55    private final AsmGenerator mGen;
56    /** Keep all classes that derive from these one (these included). */
57    private final String[] mDeriveFrom;
58    /** Glob patterns of classes to keep, e.g. "com.foo.*" */
59    private final String[] mIncludeGlobs;
60
61    /**
62     * Creates a new analyzer.
63     *
64     * @param log The log output.
65     * @param osJarPath The input source JARs to parse.
66     * @param gen The generator to fill with the class list and dependency list.
67     * @param deriveFrom Keep all classes that derive from these one (these included).
68     * @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*"
69     *        ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
70     */
71    public AsmAnalyzer(Log log, List<String> osJarPath, AsmGenerator gen,
72            String[] deriveFrom, String[] includeGlobs) {
73        mLog = log;
74        mGen = gen;
75        mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<String>();
76        mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
77        mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
78    }
79
80    /**
81     * Starts the analysis using parameters from the constructor.
82     * Fills the generator with classes & dependencies found.
83     */
84    public void analyze() throws IOException, LogAbortException {
85
86        AsmAnalyzer visitor = this;
87
88        Map<String, ClassReader> zipClasses = parseZip(mOsSourceJar);
89        mLog.info("Found %d classes in input JAR%s.", zipClasses.size(),
90                mOsSourceJar.size() > 1 ? "s" : "");
91
92        Map<String, ClassReader> found = findIncludes(zipClasses);
93        Map<String, ClassReader> deps = findDeps(zipClasses, found);
94
95        if (mGen != null) {
96            mGen.setKeep(found);
97            mGen.setDeps(deps);
98        }
99    }
100
101    /**
102     * Parses a JAR file and returns a list of all classes founds using a map
103     * class name => ASM ClassReader. Class names are in the form "android.view.View".
104     */
105    Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException {
106        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
107
108        for (String jarPath : jarPathList) {
109            ZipFile zip = new ZipFile(jarPath);
110            Enumeration<? extends ZipEntry> entries = zip.entries();
111            ZipEntry entry;
112            while (entries.hasMoreElements()) {
113                entry = entries.nextElement();
114                if (entry.getName().endsWith(".class")) {
115                    ClassReader cr = new ClassReader(zip.getInputStream(entry));
116                    String className = classReaderToClassName(cr);
117                    classes.put(className, cr);
118                }
119            }
120        }
121
122        return classes;
123    }
124
125    /**
126     * Utility that returns the fully qualified binary class name for a ClassReader.
127     * E.g. it returns something like android.view.View.
128     */
129    static String classReaderToClassName(ClassReader classReader) {
130        if (classReader == null) {
131            return null;
132        } else {
133            return classReader.getClassName().replace('/', '.');
134        }
135    }
136
137    /**
138     * Utility that returns the fully qualified binary class name from a path-like FQCN.
139     * E.g. it returns android.view.View from android/view/View.
140     */
141    static String internalToBinaryClassName(String className) {
142        if (className == null) {
143            return null;
144        } else {
145            return className.replace('/', '.');
146        }
147    }
148
149    /**
150     * Process the "includes" arrays.
151     * <p/>
152     * This updates the in_out_found map.
153     */
154    Map<String, ClassReader> findIncludes(Map<String, ClassReader> zipClasses)
155            throws LogAbortException {
156        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
157
158        mLog.debug("Find classes to include.");
159
160        for (String s : mIncludeGlobs) {
161            findGlobs(s, zipClasses, found);
162        }
163        for (String s : mDeriveFrom) {
164            findClassesDerivingFrom(s, zipClasses, found);
165        }
166
167        return found;
168    }
169
170
171    /**
172     * Uses ASM to find the class reader for the given FQCN class name.
173     * If found, insert it in the in_out_found map.
174     * Returns the class reader object.
175     */
176    ClassReader findClass(String className, Map<String, ClassReader> zipClasses,
177            Map<String, ClassReader> inOutFound) throws LogAbortException {
178        ClassReader classReader = zipClasses.get(className);
179        if (classReader == null) {
180            throw new LogAbortException("Class %s not found by ASM in %s",
181                    className, mOsSourceJar);
182        }
183
184        inOutFound.put(className, classReader);
185        return classReader;
186    }
187
188    /**
189     * Insert in the inOutFound map all classes found in zipClasses that match the
190     * given glob pattern.
191     * <p/>
192     * The glob pattern is not a regexp. It only accepts the "*" keyword to mean
193     * "anything but a period". The "." and "$" characters match themselves.
194     * The "**" keyword means everything including ".".
195     * <p/>
196     * Examples:
197     * <ul>
198     * <li>com.foo.* matches all classes in the package com.foo but NOT sub-packages.
199     * <li>com.foo*.*$Event matches all internal Event classes in a com.foo*.* class.
200     * </ul>
201     */
202    void findGlobs(String globPattern, Map<String, ClassReader> zipClasses,
203            Map<String, ClassReader> inOutFound) throws LogAbortException {
204        // transforms the glob pattern in a regexp:
205        // - escape "." with "\."
206        // - replace "*" by "[^.]*"
207        // - escape "$" with "\$"
208        // - add end-of-line match $
209        globPattern = globPattern.replaceAll("\\$", "\\\\\\$");
210        globPattern = globPattern.replaceAll("\\.", "\\\\.");
211        // prevent ** from being altered by the next rule, then process the * rule and finally
212        // the real ** rule (which is now @)
213        globPattern = globPattern.replaceAll("\\*\\*", "@");
214        globPattern = globPattern.replaceAll("\\*", "[^.]*");
215        globPattern = globPattern.replaceAll("@", ".*");
216        globPattern += "$";
217
218        Pattern regexp = Pattern.compile(globPattern);
219
220        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
221            String class_name = entry.getKey();
222            if (regexp.matcher(class_name).matches()) {
223                findClass(class_name, zipClasses, inOutFound);
224            }
225        }
226    }
227
228    /**
229     * Checks all the classes defined in the JarClassName instance and uses BCEL to
230     * determine if they are derived from the given FQCN super class name.
231     * Inserts the super class and all the class objects found in the map.
232     */
233    void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses,
234            Map<String, ClassReader> inOutFound) throws LogAbortException {
235        ClassReader super_clazz = findClass(super_name, zipClasses, inOutFound);
236
237        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
238            String className = entry.getKey();
239            if (super_name.equals(className)) {
240                continue;
241            }
242            ClassReader classReader = entry.getValue();
243            ClassReader parent_cr = classReader;
244            while (parent_cr != null) {
245                String parent_name = internalToBinaryClassName(parent_cr.getSuperName());
246                if (parent_name == null) {
247                    // not found
248                    break;
249                } else if (super_name.equals(parent_name)) {
250                    inOutFound.put(className, classReader);
251                    break;
252                }
253                parent_cr = zipClasses.get(parent_name);
254            }
255        }
256    }
257
258    /**
259     * Instantiates a new DependencyVisitor. Useful for unit tests.
260     */
261    DependencyVisitor getVisitor(Map<String, ClassReader> zipClasses,
262            Map<String, ClassReader> inKeep,
263            Map<String, ClassReader> outKeep,
264            Map<String, ClassReader> inDeps,
265            Map<String, ClassReader> outDeps) {
266        return new DependencyVisitor(zipClasses, inKeep, outKeep, inDeps, outDeps);
267    }
268
269    /**
270     * Finds all dependencies for all classes in keepClasses which are also
271     * listed in zipClasses. Returns a map of all the dependencies found.
272     */
273    Map<String, ClassReader> findDeps(Map<String, ClassReader> zipClasses,
274            Map<String, ClassReader> inOutKeepClasses) {
275
276        TreeMap<String, ClassReader> deps = new TreeMap<String, ClassReader>();
277        TreeMap<String, ClassReader> new_deps = new TreeMap<String, ClassReader>();
278        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
279        TreeMap<String, ClassReader> temp = new TreeMap<String, ClassReader>();
280
281        DependencyVisitor visitor = getVisitor(zipClasses,
282                inOutKeepClasses, new_keep,
283                deps, new_deps);
284
285        for (ClassReader cr : inOutKeepClasses.values()) {
286            cr.accept(visitor, 0 /* flags */);
287        }
288
289        while (new_deps.size() > 0 || new_keep.size() > 0) {
290            deps.putAll(new_deps);
291            inOutKeepClasses.putAll(new_keep);
292
293            temp.clear();
294            temp.putAll(new_deps);
295            temp.putAll(new_keep);
296            new_deps.clear();
297            new_keep.clear();
298            mLog.debug("Found %1$d to keep, %2$d dependencies.",
299                    inOutKeepClasses.size(), deps.size());
300
301            for (ClassReader cr : temp.values()) {
302                cr.accept(visitor, 0 /* flags */);
303            }
304        }
305
306        mLog.info("Found %1$d classes to keep, %2$d class dependencies.",
307                inOutKeepClasses.size(), deps.size());
308
309        return deps;
310    }
311
312
313
314    // ----------------------------------
315
316    /**
317     * Visitor to collect all the type dependencies from a class.
318     */
319    public class DependencyVisitor extends ClassVisitor {
320
321        /** All classes found in the source JAR. */
322        private final Map<String, ClassReader> mZipClasses;
323        /** Classes from which dependencies are to be found. */
324        private final Map<String, ClassReader> mInKeep;
325        /** Dependencies already known. */
326        private final Map<String, ClassReader> mInDeps;
327        /** New dependencies found by this visitor. */
328        private final Map<String, ClassReader> mOutDeps;
329        /** New classes to keep as-is found by this visitor. */
330        private final Map<String, ClassReader> mOutKeep;
331
332        /**
333         * Creates a new visitor that will find all the dependencies for the visited class.
334         * Types which are already in the zipClasses, keepClasses or inDeps are not marked.
335         * New dependencies are marked in outDeps.
336         *
337         * @param zipClasses All classes found in the source JAR.
338         * @param inKeep Classes from which dependencies are to be found.
339         * @param inDeps Dependencies already known.
340         * @param outDeps New dependencies found by this visitor.
341         */
342        public DependencyVisitor(Map<String, ClassReader> zipClasses,
343                Map<String, ClassReader> inKeep,
344                Map<String, ClassReader> outKeep,
345                Map<String,ClassReader> inDeps,
346                Map<String,ClassReader> outDeps) {
347            super(Opcodes.ASM4);
348            mZipClasses = zipClasses;
349            mInKeep = inKeep;
350            mOutKeep = outKeep;
351            mInDeps = inDeps;
352            mOutDeps = outDeps;
353        }
354
355        /**
356         * Considers the given class name as a dependency.
357         * If it does, add to the mOutDeps map.
358         */
359        public void considerName(String className) {
360            if (className == null) {
361                return;
362            }
363
364            className = internalToBinaryClassName(className);
365
366            // exclude classes that have already been found
367            if (mInKeep.containsKey(className) ||
368                    mOutKeep.containsKey(className) ||
369                    mInDeps.containsKey(className) ||
370                    mOutDeps.containsKey(className)) {
371                return;
372            }
373
374            // exclude classes that are not part of the JAR file being examined
375            ClassReader cr = mZipClasses.get(className);
376            if (cr == null) {
377                return;
378            }
379
380            try {
381                // exclude classes that are part of the default JRE (the one executing this program)
382                if (getClass().getClassLoader().loadClass(className) != null) {
383                    return;
384                }
385            } catch (ClassNotFoundException e) {
386                // ignore
387            }
388
389            // accept this class:
390            // - android classes are added to dependencies
391            // - non-android classes are added to the list of classes to keep as-is (they don't need
392            //   to be stubbed).
393            if (className.indexOf("android") >= 0) {  // TODO make configurable
394                mOutDeps.put(className, cr);
395            } else {
396                mOutKeep.put(className, cr);
397            }
398        }
399
400        /**
401         * Considers this array of names using considerName().
402         */
403        public void considerNames(String[] classNames) {
404            if (classNames != null) {
405                for (String className : classNames) {
406                    considerName(className);
407                }
408            }
409        }
410
411        /**
412         * Considers this signature or type signature by invoking the {@link SignatureVisitor}
413         * on it.
414         */
415        public void considerSignature(String signature) {
416            if (signature != null) {
417                SignatureReader sr = new SignatureReader(signature);
418                // SignatureReader.accept will call accessType so we don't really have
419                // to differentiate where the signature comes from.
420                sr.accept(new MySignatureVisitor());
421            }
422        }
423
424        /**
425         * Considers this {@link Type}. For arrays, the element type is considered.
426         * If the type is an object, it's internal name is considered.
427         */
428        public void considerType(Type t) {
429            if (t != null) {
430                if (t.getSort() == Type.ARRAY) {
431                    t = t.getElementType();
432                }
433                if (t.getSort() == Type.OBJECT) {
434                    considerName(t.getInternalName());
435                }
436            }
437        }
438
439        /**
440         * Considers a descriptor string. The descriptor is converted to a {@link Type}
441         * and then considerType() is invoked.
442         */
443        public void considerDesc(String desc) {
444            if (desc != null) {
445                try {
446                    Type t = Type.getType(desc);
447                    considerType(t);
448                } catch (ArrayIndexOutOfBoundsException e) {
449                    // ignore, not a valid type.
450                }
451            }
452        }
453
454
455        // ---------------------------------------------------
456        // --- ClassVisitor, FieldVisitor
457        // ---------------------------------------------------
458
459        // Visits a class header
460        @Override
461        public void visit(int version, int access, String name,
462                String signature, String superName, String[] interfaces) {
463            // signature is the signature of this class. May be null if the class is not a generic
464            // one, and does not extend or implement generic classes or interfaces.
465
466            if (signature != null) {
467                considerSignature(signature);
468            }
469
470            // superName is the internal of name of the super class (see getInternalName).
471            // For interfaces, the super class is Object. May be null but only for the Object class.
472            considerName(superName);
473
474            // interfaces is the internal names of the class's interfaces (see getInternalName).
475            // May be null.
476            considerNames(interfaces);
477        }
478
479
480        @Override
481        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
482            // desc is the class descriptor of the annotation class.
483            considerDesc(desc);
484            return new MyAnnotationVisitor();
485        }
486
487        @Override
488        public void visitAttribute(Attribute attr) {
489            // pass
490        }
491
492        // Visits the end of a class
493        @Override
494        public void visitEnd() {
495            // pass
496        }
497
498        private class MyFieldVisitor extends FieldVisitor {
499
500            public MyFieldVisitor() {
501                super(Opcodes.ASM4);
502            }
503
504            @Override
505            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
506                // desc is the class descriptor of the annotation class.
507                considerDesc(desc);
508                return new MyAnnotationVisitor();
509            }
510
511            @Override
512            public void visitAttribute(Attribute attr) {
513                // pass
514            }
515
516            // Visits the end of a class
517            @Override
518            public void visitEnd() {
519                // pass
520            }
521        }
522
523        @Override
524        public FieldVisitor visitField(int access, String name, String desc,
525                String signature, Object value) {
526            // desc is the field's descriptor (see Type).
527            considerDesc(desc);
528
529            // signature is the field's signature. May be null if the field's type does not use
530            // generic types.
531            considerSignature(signature);
532
533            return new MyFieldVisitor();
534        }
535
536        @Override
537        public void visitInnerClass(String name, String outerName, String innerName, int access) {
538            // name is the internal name of an inner class (see getInternalName).
539            considerName(name);
540        }
541
542        @Override
543        public MethodVisitor visitMethod(int access, String name, String desc,
544                String signature, String[] exceptions) {
545            // desc is the method's descriptor (see Type).
546            considerDesc(desc);
547            // signature is the method's signature. May be null if the method parameters, return
548            // type and exceptions do not use generic types.
549            considerSignature(signature);
550
551            return new MyMethodVisitor();
552        }
553
554        @Override
555        public void visitOuterClass(String owner, String name, String desc) {
556            // pass
557        }
558
559        @Override
560        public void visitSource(String source, String debug) {
561            // pass
562        }
563
564
565        // ---------------------------------------------------
566        // --- MethodVisitor
567        // ---------------------------------------------------
568
569        private class MyMethodVisitor extends MethodVisitor {
570
571            public MyMethodVisitor() {
572                super(Opcodes.ASM4);
573            }
574
575
576            @Override
577            public AnnotationVisitor visitAnnotationDefault() {
578                return new MyAnnotationVisitor();
579            }
580
581            @Override
582            public void visitCode() {
583                // pass
584            }
585
586            // field instruction
587            @Override
588            public void visitFieldInsn(int opcode, String owner, String name, String desc) {
589                // name is the field's name.
590                considerName(name);
591                // desc is the field's descriptor (see Type).
592                considerDesc(desc);
593            }
594
595            @Override
596            public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
597                // pass
598            }
599
600            @Override
601            public void visitIincInsn(int var, int increment) {
602                // pass -- an IINC instruction
603            }
604
605            @Override
606            public void visitInsn(int opcode) {
607                // pass -- a zero operand instruction
608            }
609
610            @Override
611            public void visitIntInsn(int opcode, int operand) {
612                // pass -- a single int operand instruction
613            }
614
615            @Override
616            public void visitJumpInsn(int opcode, Label label) {
617                // pass -- a jump instruction
618            }
619
620            @Override
621            public void visitLabel(Label label) {
622                // pass -- a label target
623            }
624
625            // instruction to load a constant from the stack
626            @Override
627            public void visitLdcInsn(Object cst) {
628                if (cst instanceof Type) {
629                    considerType((Type) cst);
630                }
631            }
632
633            @Override
634            public void visitLineNumber(int line, Label start) {
635                // pass
636            }
637
638            @Override
639            public void visitLocalVariable(String name, String desc,
640                    String signature, Label start, Label end, int index) {
641                // desc is the type descriptor of this local variable.
642                considerDesc(desc);
643                // signature is the type signature of this local variable. May be null if the local
644                // variable type does not use generic types.
645                considerSignature(signature);
646            }
647
648            @Override
649            public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
650                // pass -- a lookup switch instruction
651            }
652
653            @Override
654            public void visitMaxs(int maxStack, int maxLocals) {
655                // pass
656            }
657
658            // instruction that invokes a method
659            @Override
660            public void visitMethodInsn(int opcode, String owner, String name, String desc) {
661
662                // owner is the internal name of the method's owner class
663                considerName(owner);
664                // desc is the method's descriptor (see Type).
665                considerDesc(desc);
666            }
667
668            // instruction multianewarray, whatever that is
669            @Override
670            public void visitMultiANewArrayInsn(String desc, int dims) {
671
672                // desc an array type descriptor.
673                considerDesc(desc);
674            }
675
676            @Override
677            public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
678                    boolean visible) {
679                // desc is the class descriptor of the annotation class.
680                considerDesc(desc);
681                return new MyAnnotationVisitor();
682            }
683
684            @Override
685            public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
686                // pass -- table switch instruction
687
688            }
689
690            @Override
691            public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
692                // type is the internal name of the type of exceptions handled by the handler,
693                // or null to catch any exceptions (for "finally" blocks).
694                considerName(type);
695            }
696
697            // type instruction
698            @Override
699            public void visitTypeInsn(int opcode, String type) {
700                // type is the operand of the instruction to be visited. This operand must be the
701                // internal name of an object or array class.
702                considerName(type);
703            }
704
705            @Override
706            public void visitVarInsn(int opcode, int var) {
707                // pass -- local variable instruction
708            }
709        }
710
711        private class MySignatureVisitor extends SignatureVisitor {
712
713            public MySignatureVisitor() {
714                super(Opcodes.ASM4);
715            }
716
717            // ---------------------------------------------------
718            // --- SignatureVisitor
719            // ---------------------------------------------------
720
721            private String mCurrentSignatureClass = null;
722
723            // Starts the visit of a signature corresponding to a class or interface type
724            @Override
725            public void visitClassType(String name) {
726                mCurrentSignatureClass = name;
727                considerName(name);
728            }
729
730            // Visits an inner class
731            @Override
732            public void visitInnerClassType(String name) {
733                if (mCurrentSignatureClass != null) {
734                    mCurrentSignatureClass += "$" + name;
735                    considerName(mCurrentSignatureClass);
736                }
737            }
738
739            @Override
740            public SignatureVisitor visitArrayType() {
741                return new MySignatureVisitor();
742            }
743
744            @Override
745            public void visitBaseType(char descriptor) {
746                // pass -- a primitive type, ignored
747            }
748
749            @Override
750            public SignatureVisitor visitClassBound() {
751                return new MySignatureVisitor();
752            }
753
754            @Override
755            public SignatureVisitor visitExceptionType() {
756                return new MySignatureVisitor();
757            }
758
759            @Override
760            public void visitFormalTypeParameter(String name) {
761                // pass
762            }
763
764            @Override
765            public SignatureVisitor visitInterface() {
766                return new MySignatureVisitor();
767            }
768
769            @Override
770            public SignatureVisitor visitInterfaceBound() {
771                return new MySignatureVisitor();
772            }
773
774            @Override
775            public SignatureVisitor visitParameterType() {
776                return new MySignatureVisitor();
777            }
778
779            @Override
780            public SignatureVisitor visitReturnType() {
781                return new MySignatureVisitor();
782            }
783
784            @Override
785            public SignatureVisitor visitSuperclass() {
786                return new MySignatureVisitor();
787            }
788
789            @Override
790            public SignatureVisitor visitTypeArgument(char wildcard) {
791                return new MySignatureVisitor();
792            }
793
794            @Override
795            public void visitTypeVariable(String name) {
796                // pass
797            }
798
799            @Override
800            public void visitTypeArgument() {
801                // pass
802            }
803        }
804
805
806        // ---------------------------------------------------
807        // --- AnnotationVisitor
808        // ---------------------------------------------------
809
810        private class MyAnnotationVisitor extends AnnotationVisitor {
811
812            public MyAnnotationVisitor() {
813                super(Opcodes.ASM4);
814            }
815
816            // Visits a primitive value of an annotation
817            @Override
818            public void visit(String name, Object value) {
819                // value is the actual value, whose type must be Byte, Boolean, Character, Short,
820                // Integer, Long, Float, Double, String or Type
821                if (value instanceof Type) {
822                    considerType((Type) value);
823                }
824            }
825
826            @Override
827            public AnnotationVisitor visitAnnotation(String name, String desc) {
828                // desc is the class descriptor of the nested annotation class.
829                considerDesc(desc);
830                return new MyAnnotationVisitor();
831            }
832
833            @Override
834            public AnnotationVisitor visitArray(String name) {
835                return new MyAnnotationVisitor();
836            }
837
838            @Override
839            public void visitEnum(String name, String desc, String value) {
840                // desc is the class descriptor of the enumeration class.
841                considerDesc(desc);
842            }
843        }
844    }
845}
846