View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.annotations;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URI;
24  import java.net.URL;
25  import java.net.URLClassLoader;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.Set;
32  import java.util.jar.JarEntry;
33  import java.util.jar.JarInputStream;
34  
35  import org.eclipse.jetty.util.Loader;
36  import org.eclipse.jetty.util.log.Log;
37  import org.eclipse.jetty.util.log.Logger;
38  import org.eclipse.jetty.util.resource.Resource;
39  import org.eclipse.jetty.webapp.JarScanner;
40  import org.objectweb.asm.AnnotationVisitor;
41  import org.objectweb.asm.ClassReader;
42  import org.objectweb.asm.FieldVisitor;
43  import org.objectweb.asm.MethodVisitor;
44  import org.objectweb.asm.commons.EmptyVisitor;
45  
46  /**
47   * AnnotationParser
48   *
49   * Use asm to scan classes for annotations. A SAX-style parsing is done, with
50   * a handler being able to be registered to handle each annotation type.
51   */
52  public class AnnotationParser
53  {
54      private static final Logger LOG = Log.getLogger(AnnotationParser.class);
55  
56      protected Set<String> _parsedClassNames = new HashSet<String>();
57      protected List<Handler> _handlers = new ArrayList<Handler>();
58  
59      public static String normalize (String name)
60      {
61          if (name==null)
62              return null;
63  
64          if (name.startsWith("L") && name.endsWith(";"))
65              name = name.substring(1, name.length()-1);
66  
67          if (name.endsWith(".class"))
68              name = name.substring(0, name.length()-".class".length());
69  
70          return name.replace('/', '.');
71      }
72  
73  
74  
75      public abstract class Value
76      {
77          String _name;
78  
79          public Value (String name)
80          {
81              _name = name;
82          }
83  
84          public String getName()
85          {
86              return _name;
87          }
88  
89          public abstract Object getValue();
90  
91      }
92  
93  
94  
95  
96      public class SimpleValue extends Value
97      {
98          Object _val;
99  
100         public SimpleValue(String name)
101         {
102             super(name);
103         }
104 
105         public void setValue(Object val)
106         {
107             _val=val;
108         }
109         @Override
110         public Object getValue()
111         {
112             return _val;
113         }
114 
115         @Override
116         public String toString()
117         {
118             return "("+getName()+":"+_val+")";
119         }
120     }
121 
122     public class ListValue extends Value
123     {
124         List<Value> _val;
125 
126         public ListValue (String name)
127         {
128             super(name);
129             _val = new ArrayList<Value>();
130         }
131 
132         @Override
133         public Object getValue()
134         {
135             return _val;
136         }
137 
138         public List<Value> getList()
139         {
140             return _val;
141         }
142 
143         public void addValue (Value v)
144         {
145             _val.add(v);
146         }
147 
148         public int size ()
149         {
150             return _val.size();
151         }
152 
153         @Override
154         public String toString()
155         {
156             StringBuffer buff = new StringBuffer();
157             buff.append("(");
158             buff.append(getName());
159             buff.append(":");
160             for (Value n: _val)
161             {
162                 buff.append(" "+n.toString());
163             }
164             buff.append(")");
165 
166             return buff.toString();
167         }
168     }
169 
170 
171 
172     /**
173      * Handler
174      *
175      * Signature for all handlers that respond to parsing class files.
176      */
177     public interface Handler
178     {
179        
180     }
181     
182     
183     
184     /**
185      * DiscoverableAnnotationHandler
186      *
187      * Processes an annotation when it is discovered on a class.
188      */
189     public interface DiscoverableAnnotationHandler extends Handler
190     {
191         /**
192          * Process an annotation that was discovered on a class
193          * @param className
194          * @param version
195          * @param access
196          * @param signature
197          * @param superName
198          * @param interfaces
199          * @param annotation
200          * @param values
201          */
202         public void handleClass (String className, int version, int access,
203                                  String signature, String superName, String[] interfaces,
204                                  String annotation, List<Value>values);
205 
206         /**
207          * Process an annotation that was discovered on a method
208          * @param className
209          * @param methodName
210          * @param access
211          * @param desc
212          * @param signature
213          * @param exceptions
214          * @param annotation
215          * @param values
216          */
217         public void handleMethod (String className, String methodName, int access,
218                                   String desc, String signature,String[] exceptions,
219                                   String annotation, List<Value>values);
220 
221         
222         /**
223          * Process an annotation that was discovered on a field
224          * @param className
225          * @param fieldName
226          * @param access
227          * @param fieldType
228          * @param signature
229          * @param value
230          * @param annotation
231          * @param values
232          */
233         public void handleField (String className, String fieldName,  int access,
234                                  String fieldType, String signature, Object value,
235                                  String annotation, List<Value>values);
236         
237         
238         /**
239          * Get the name of the annotation processed by this handler. Can be null
240          * 
241          * @return
242          */
243         public String getAnnotationName();
244     }
245 
246 
247     
248     /**
249      * ClassHandler
250      *
251      * Responds to finding a Class
252      */
253     public interface ClassHandler extends Handler
254     {
255         public void handle (String className, int version, int access, String signature, String superName, String[] interfaces);
256     }
257 
258     
259     
260     /**
261      * MethodHandler
262      *
263      * Responds to finding a Method
264      */
265     public interface MethodHandler extends Handler
266     {
267         public void handle (String className, String methodName, int access,  String desc, String signature,String[] exceptions);
268     }
269 
270     
271     /**
272      * FieldHandler
273      *
274      * Responds to finding a Field
275      */
276     public interface FieldHandler extends Handler
277     {
278         public void handle (String className, String fieldName, int access, String fieldType, String signature, Object value);
279     }
280 
281     
282     
283     /**
284      * MyAnnotationVisitor
285      *
286      * ASM Visitor for Annotations
287      */
288     public class MyAnnotationVisitor implements AnnotationVisitor
289     {
290         List<Value> _annotationValues;
291         String _annotationName;
292 
293         public MyAnnotationVisitor (String annotationName, List<Value> values)
294         {
295             _annotationValues = values;
296             _annotationName = annotationName;
297         }
298 
299         public List<Value> getAnnotationValues()
300         {
301             return _annotationValues;
302         }
303 
304         /**
305          * Visit a single-valued (name,value) pair for this annotation
306          * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
307          */
308         @Override
309         public void visit(String aname, Object avalue)
310         {
311            SimpleValue v = new SimpleValue(aname);
312            v.setValue(avalue);
313            _annotationValues.add(v);
314         }
315 
316         /**
317          * Visit a (name,value) pair whose value is another Annotation
318          * @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang.String, java.lang.String)
319          */
320         @Override
321         public AnnotationVisitor visitAnnotation(String name, String desc)
322         {
323             String s = normalize(desc);
324             ListValue v = new ListValue(s);
325             _annotationValues.add(v);
326             MyAnnotationVisitor visitor = new MyAnnotationVisitor(s, v.getList());
327             return visitor;
328         }
329 
330         /**
331          * Visit an array valued (name, value) pair for this annotation
332          * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
333          */
334         @Override
335         public AnnotationVisitor visitArray(String name)
336         {
337             ListValue v = new ListValue(name);
338             _annotationValues.add(v);
339             MyAnnotationVisitor visitor = new MyAnnotationVisitor(null, v.getList());
340             return visitor;
341         }
342 
343         /**
344          * Visit a enum-valued (name,value) pair for this annotation
345          * @see org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String, java.lang.String, java.lang.String)
346          */
347         @Override
348         public void visitEnum(String name, String desc, String value)
349         {
350             //TODO
351         }
352 
353         @Override
354         public void visitEnd()
355         {
356         }
357     }
358 
359 
360 
361 
362     /**
363      * MyClassVisitor
364      *
365      * ASM visitor for a class.
366      */
367     public class MyClassVisitor extends EmptyVisitor
368     {
369         String _className;
370         int _access;
371         String _signature;
372         String _superName;
373         String[] _interfaces;
374         int _version;
375 
376 
377         @Override
378         public void visit (int version,
379                            final int access,
380                            final String name,
381                            final String signature,
382                            final String superName,
383                            final String[] interfaces)
384         {
385             _className = normalize(name);
386             _access = access;
387             _signature = signature;
388             _superName = superName;
389             _interfaces = interfaces;
390             _version = version;
391 
392             _parsedClassNames.add(_className);
393             //call all registered ClassHandlers
394             String[] normalizedInterfaces = null;
395             if (interfaces!= null)
396             {
397                 normalizedInterfaces = new String[interfaces.length];
398                 int i=0;
399                 for (String s : interfaces)
400                     normalizedInterfaces[i++] = normalize(s);
401             }
402 
403             for (Handler h : AnnotationParser.this._handlers)
404             {
405                 if (h instanceof ClassHandler)
406                 {
407                     ((ClassHandler)h).handle(_className, _version, _access, _signature, normalize(_superName), normalizedInterfaces);
408                 }
409             }
410         }
411 
412         @Override
413         public AnnotationVisitor visitAnnotation (String desc, boolean visible)
414         {
415             MyAnnotationVisitor visitor = new MyAnnotationVisitor(normalize(desc), new ArrayList<Value>())
416             {
417                 @Override
418                 public void visitEnd()
419                 {
420                     super.visitEnd();
421 
422                     //call all AnnotationHandlers with classname, annotation name + values
423                     for (Handler h : AnnotationParser.this._handlers)
424                     {
425                         if (h instanceof DiscoverableAnnotationHandler)
426                         {
427                             DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
428                             if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
429                                 dah.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
430                         }
431                     }
432                 }
433             };
434 
435             return visitor;
436         }
437 
438         @Override
439         public MethodVisitor visitMethod (final int access,
440                                           final String name,
441                                           final String methodDesc,
442                                           final String signature,
443                                           final String[] exceptions)
444         {
445 
446             return new EmptyVisitor ()
447             {
448                 @Override
449                 public AnnotationVisitor visitAnnotation(String desc, boolean visible)
450                 {
451                     MyAnnotationVisitor visitor = new MyAnnotationVisitor (normalize(desc), new ArrayList<Value>())
452                     {
453                         @Override
454                         public void visitEnd()
455                         {
456                             super.visitEnd();
457                             //call all AnnotationHandlers with classname, method, annotation name + values
458                             for (Handler h : AnnotationParser.this._handlers)
459                             {
460                                 if (h instanceof DiscoverableAnnotationHandler)
461                                 {
462                                     DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
463                                     if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
464                                         dah.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues);
465                                 }
466                             }
467                         }
468                     };
469 
470                     return visitor;
471                 }
472             };
473         }
474 
475         @Override
476         public FieldVisitor visitField (final int access,
477                                         final String fieldName,
478                                         final String fieldType,
479                                         final String signature,
480                                         final Object value)
481         {
482 
483             return new EmptyVisitor ()
484             {
485                 @Override
486                 public AnnotationVisitor visitAnnotation(String desc, boolean visible)
487                 {
488                     MyAnnotationVisitor visitor = new MyAnnotationVisitor(normalize(desc), new ArrayList<Value>())
489                     {
490                         @Override
491                         public void visitEnd()
492                         {
493                             super.visitEnd();
494                             for (Handler h : AnnotationParser.this._handlers)
495                             {
496                                 if (h instanceof DiscoverableAnnotationHandler)
497                                 {
498                                     DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
499                                     if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
500                                         dah.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
501                                 }
502                             }
503                         }
504                     };
505                     return visitor;
506                 }
507             };
508         }
509     }
510 
511 
512     /**
513      * Register a handler that will be called back when the named annotation is
514      * encountered on a class.
515      *
516      * @deprecated see registerHandler(Handler)
517      * @param annotationName
518      * @param handler
519      */
520     @Deprecated
521     public void registerAnnotationHandler (String annotationName, DiscoverableAnnotationHandler handler)
522     {
523         _handlers.add(handler);
524     }
525 
526     
527     /**
528      * @deprecated
529      * @param annotationName
530      * @return
531      */
532     @Deprecated
533     public List<DiscoverableAnnotationHandler> getAnnotationHandlers(String annotationName)
534     {
535         List<DiscoverableAnnotationHandler> handlers = new ArrayList<DiscoverableAnnotationHandler>();
536         for (Handler h:_handlers)
537         {
538             if (h instanceof DiscoverableAnnotationHandler)
539             {
540                 DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
541                 if (annotationName.equals(dah.getAnnotationName()))
542                     handlers.add(dah);
543             }
544         }
545  
546         return handlers;
547     }
548 
549     /**
550      * @deprecated
551      * @return
552      */
553     @Deprecated
554     public List<DiscoverableAnnotationHandler> getAnnotationHandlers()
555     {
556         List<DiscoverableAnnotationHandler> allAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
557         for (Handler h:_handlers)
558         {
559             if (h instanceof DiscoverableAnnotationHandler)
560             allAnnotationHandlers.add((DiscoverableAnnotationHandler)h);
561         }
562         return allAnnotationHandlers;
563     }
564 
565     /**
566      * @deprecated see registerHandler(Handler)
567      * @param handler
568      */
569     @Deprecated
570     public void registerClassHandler (ClassHandler handler)
571     {
572         _handlers.add(handler);
573     }
574     
575     
576     
577     /**
578      * Add a particular handler
579      * 
580      * @param h
581      */
582     public void registerHandler(Handler h)
583     {
584         if (h == null)
585             return;
586         
587         _handlers.add(h);
588     }
589     
590     
591     /**
592      * Add a list of handlers
593      * 
594      * @param handlers
595      */
596     public void registerHandlers(List<? extends Handler> handlers)
597     {
598         if (handlers == null)
599             return;
600         _handlers.addAll(handlers);
601     }
602     
603     
604     /**
605      * Remove a particular handler
606      * 
607      * @param h
608      * @return
609      */
610     public boolean deregisterHandler(Handler h)
611     {
612         return _handlers.remove(h);
613     }
614     
615     
616     /**
617      * Remove all registered handlers
618      */
619     public void clearHandlers()
620     {
621         _handlers.clear();
622     }
623     
624 
625     /**
626      * True if the class has already been processed, false otherwise
627      * @param className
628      * @return
629      */
630     public boolean isParsed (String className)
631     {
632         return _parsedClassNames.contains(className);
633     }
634 
635     
636     
637     /**
638      * Parse a given class
639      * 
640      * @param className
641      * @param resolver
642      * @throws Exception
643      */
644     public void parse (String className, ClassNameResolver resolver)
645     throws Exception
646     {
647         if (className == null)
648             return;
649 
650         if (!resolver.isExcluded(className))
651         {
652             if (!isParsed(className) || resolver.shouldOverride(className))
653             {
654                 className = className.replace('.', '/')+".class";
655                 URL resource = Loader.getResource(this.getClass(), className, false);
656                 if (resource!= null)
657                 {
658                     Resource r = Resource.newResource(resource);
659                     scanClass(r.getInputStream());
660                 }
661             }
662         }
663     }
664 
665     
666     
667     /**
668      * Parse the given class, optionally walking its inheritance hierarchy
669      * 
670      * @param clazz
671      * @param resolver
672      * @param visitSuperClasses
673      * @throws Exception
674      */
675     public void parse (Class<?> clazz, ClassNameResolver resolver, boolean visitSuperClasses)
676     throws Exception
677     {
678         Class<?> cz = clazz;
679         while (cz != null)
680         {
681             if (!resolver.isExcluded(cz.getName()))
682             {
683                 if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName()))
684                 {
685                     String nameAsResource = cz.getName().replace('.', '/')+".class";
686                     URL resource = Loader.getResource(this.getClass(), nameAsResource, false);
687                     if (resource!= null)
688                     {
689                         Resource r = Resource.newResource(resource);
690                         scanClass(r.getInputStream());
691                     }
692                 }
693             }
694             if (visitSuperClasses)
695                 cz = cz.getSuperclass();
696             else
697                 cz = null;
698         }
699     }
700 
701     
702     
703     /**
704      * Parse the given classes
705      * 
706      * @param classNames
707      * @param resolver
708      * @throws Exception
709      */
710     public void parse (String[] classNames, ClassNameResolver resolver)
711     throws Exception
712     {
713         if (classNames == null)
714             return;
715 
716         parse(Arrays.asList(classNames), resolver);
717     }
718 
719     
720     /**
721      * Parse the given classes
722      * 
723      * @param classNames
724      * @param resolver
725      * @throws Exception
726      */
727     public void parse (List<String> classNames, ClassNameResolver resolver)
728     throws Exception
729     {
730         for (String s:classNames)
731         {
732             if ((resolver == null) || (!resolver.isExcluded(s) &&  (!isParsed(s) || resolver.shouldOverride(s))))
733             {
734                 s = s.replace('.', '/')+".class";
735                 URL resource = Loader.getResource(this.getClass(), s, false);
736                 if (resource!= null)
737                 {
738                     Resource r = Resource.newResource(resource);
739                     scanClass(r.getInputStream());
740                 }
741             }
742         }
743     }
744 
745     
746     /**
747      * Parse all classes in a directory
748      * 
749      * @param dir
750      * @param resolver
751      * @throws Exception
752      */
753     public void parseDir (Resource dir, ClassNameResolver resolver)
754     throws Exception
755     {
756         if (!dir.isDirectory() || !dir.exists())
757             return;
758 
759         if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
760         
761         String[] files=dir.list();
762         for (int f=0;files!=null && f<files.length;f++)
763         {
764             try
765             {
766                 Resource res = dir.addPath(files[f]);
767                 if (res.isDirectory())
768                     parseDir(res, resolver);
769                 String name = res.getName();
770                 if (name.endsWith(".class"))
771                 {
772                     if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
773                     {
774                         Resource r = Resource.newResource(res.getURL());
775                         if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
776                         scanClass(r.getInputStream());
777                     }
778 
779                 }
780             }
781             catch (Exception ex)
782             {
783                 LOG.warn(Log.EXCEPTION,ex);
784             }
785         }
786     }
787 
788 
789     /**
790      * Parse classes in the supplied classloader. 
791      * Only class files in jar files will be scanned.
792      * 
793      * @param loader
794      * @param visitParents
795      * @param nullInclusive
796      * @param resolver
797      * @throws Exception
798      */
799     public void parse (ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver)
800     throws Exception
801     {
802         if (loader==null)
803             return;
804 
805         if (!(loader instanceof URLClassLoader))
806             return; //can't extract classes?
807 
808         JarScanner scanner = new JarScanner()
809         {
810             @Override
811             public void processEntry(URI jarUri, JarEntry entry)
812             {
813                 try
814                 {
815                     String name = entry.getName();
816                     if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
817                     {
818                         String shortName =  name.replace('/', '.').substring(0,name.length()-6);
819                         if ((resolver == null)
820                              ||
821                             (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
822                         {
823 
824                             Resource clazz = Resource.newResource("jar:"+jarUri+"!/"+name);
825                             scanClass(clazz.getInputStream());
826                         }
827                     }
828                 }
829                 catch (Exception e)
830                 {
831                     LOG.warn("Problem processing jar entry "+entry, e);
832                 }
833             }
834 
835         };
836 
837         scanner.scan(null, loader, nullInclusive, visitParents);
838     }
839 
840 
841     /**
842      * Parse classes in the supplied uris.
843      * 
844      * @param uris
845      * @param resolver
846      * @throws Exception
847      */
848     public void parse (URI[] uris, final ClassNameResolver resolver)
849     throws Exception
850     {
851         if (uris==null)
852             return;
853 
854         for (URI uri:uris)
855         {
856             try
857             {
858                 parse(uri, resolver);
859             }
860             catch (Exception e)
861             {
862                 LOG.warn("Problem parsing classes from {}", uri);
863             }
864         }
865 
866     }
867 
868     /**
869      * Parse a particular resource
870      * @param uri
871      * @param resolver
872      * @throws Exception
873      */
874     public void parse (URI uri, final ClassNameResolver resolver)
875     throws Exception
876     {
877         if (uri == null)
878             return;
879 
880         Resource r = Resource.newResource(uri);
881         if (r.exists() && r.isDirectory())
882         {
883             parseDir(r, resolver);
884             return;
885         }
886 
887         String fullname = r.toString();
888         if (fullname.endsWith(".jar"))
889         {
890             parseJar(r, resolver);
891             return;
892         }
893 
894         if (fullname.endsWith(".class"))
895         {
896             scanClass(r.getInputStream());
897             return;
898         }
899     }
900 
901 
902 
903     /**
904      * Parse a resource that is a jar file.
905      * 
906      * @param jarResource
907      * @param resolver
908      * @throws Exception
909      */
910     public void parseJar (Resource jarResource,  final ClassNameResolver resolver)
911     throws Exception
912     {
913         if (jarResource == null)
914             return;
915         
916         URI uri = jarResource.getURI();
917         
918         if (jarResource.toString().endsWith(".jar"))
919         {
920             if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);};
921             
922             //treat it as a jar that we need to open and scan all entries from             
923             InputStream in = jarResource.getInputStream();
924             if (in==null)
925                 return;
926 
927             JarInputStream jar_in = new JarInputStream(in);
928             try
929             { 
930                 JarEntry entry = jar_in.getNextJarEntry();
931                 while (entry!=null)
932                 {                   
933                     try
934                     {
935                         String name = entry.getName();
936                         if (name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
937                         {
938                             String shortName =  name.replace('/', '.').substring(0,name.length()-6);
939 
940                             if ((resolver == null)
941                                  ||
942                                 (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
943                             {
944                                 Resource clazz = Resource.newResource("jar:"+uri+"!/"+name);
945                                 if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
946                                 scanClass(clazz.getInputStream());
947                             }
948                         }
949                     }
950                     catch (Exception e)
951                     {
952                         LOG.warn("Problem processing jar entry "+entry, e);
953                     }
954                                     
955                     entry = jar_in.getNextJarEntry();
956                 }
957             }
958             finally
959             {
960                 jar_in.close();
961             } 
962         }   
963     }
964     
965     
966     /**
967      * Use ASM on a class
968      * 
969      * @param is
970      * @throws IOException
971      */
972     protected void scanClass (InputStream is)
973     throws IOException
974     {
975         ClassReader reader = new ClassReader(is);
976         reader.accept(new MyClassVisitor(), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
977     }
978 }
979