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         public String getAnnotationName();
242     }
243 
244 
245     
246     /**
247      * ClassHandler
248      *
249      * Responds to finding a Class
250      */
251     public interface ClassHandler extends Handler
252     {
253         public void handle (String className, int version, int access, String signature, String superName, String[] interfaces);
254     }
255 
256     
257     
258     /**
259      * MethodHandler
260      *
261      * Responds to finding a Method
262      */
263     public interface MethodHandler extends Handler
264     {
265         public void handle (String className, String methodName, int access,  String desc, String signature,String[] exceptions);
266     }
267 
268     
269     /**
270      * FieldHandler
271      *
272      * Responds to finding a Field
273      */
274     public interface FieldHandler extends Handler
275     {
276         public void handle (String className, String fieldName, int access, String fieldType, String signature, Object value);
277     }
278 
279     
280     
281     /**
282      * MyAnnotationVisitor
283      *
284      * ASM Visitor for Annotations
285      */
286     public class MyAnnotationVisitor implements AnnotationVisitor
287     {
288         List<Value> _annotationValues;
289         String _annotationName;
290 
291         public MyAnnotationVisitor (String annotationName, List<Value> values)
292         {
293             _annotationValues = values;
294             _annotationName = annotationName;
295         }
296 
297         public List<Value> getAnnotationValues()
298         {
299             return _annotationValues;
300         }
301 
302         /**
303          * Visit a single-valued (name,value) pair for this annotation
304          * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
305          */
306         @Override
307         public void visit(String aname, Object avalue)
308         {
309            SimpleValue v = new SimpleValue(aname);
310            v.setValue(avalue);
311            _annotationValues.add(v);
312         }
313 
314         /**
315          * Visit a (name,value) pair whose value is another Annotation
316          * @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang.String, java.lang.String)
317          */
318         @Override
319         public AnnotationVisitor visitAnnotation(String name, String desc)
320         {
321             String s = normalize(desc);
322             ListValue v = new ListValue(s);
323             _annotationValues.add(v);
324             MyAnnotationVisitor visitor = new MyAnnotationVisitor(s, v.getList());
325             return visitor;
326         }
327 
328         /**
329          * Visit an array valued (name, value) pair for this annotation
330          * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
331          */
332         @Override
333         public AnnotationVisitor visitArray(String name)
334         {
335             ListValue v = new ListValue(name);
336             _annotationValues.add(v);
337             MyAnnotationVisitor visitor = new MyAnnotationVisitor(null, v.getList());
338             return visitor;
339         }
340 
341         /**
342          * Visit a enum-valued (name,value) pair for this annotation
343          * @see org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String, java.lang.String, java.lang.String)
344          */
345         @Override
346         public void visitEnum(String name, String desc, String value)
347         {
348             //TODO
349         }
350 
351         @Override
352         public void visitEnd()
353         {
354         }
355     }
356 
357 
358 
359 
360     /**
361      * MyClassVisitor
362      *
363      * ASM visitor for a class.
364      */
365     public class MyClassVisitor extends EmptyVisitor
366     {
367         String _className;
368         int _access;
369         String _signature;
370         String _superName;
371         String[] _interfaces;
372         int _version;
373 
374 
375         @Override
376         public void visit (int version,
377                            final int access,
378                            final String name,
379                            final String signature,
380                            final String superName,
381                            final String[] interfaces)
382         {
383             _className = normalize(name);
384             _access = access;
385             _signature = signature;
386             _superName = superName;
387             _interfaces = interfaces;
388             _version = version;
389 
390             _parsedClassNames.add(_className);
391             //call all registered ClassHandlers
392             String[] normalizedInterfaces = null;
393             if (interfaces!= null)
394             {
395                 normalizedInterfaces = new String[interfaces.length];
396                 int i=0;
397                 for (String s : interfaces)
398                     normalizedInterfaces[i++] = normalize(s);
399             }
400 
401             for (Handler h : AnnotationParser.this._handlers)
402             {
403                 if (h instanceof ClassHandler)
404                 {
405                     ((ClassHandler)h).handle(_className, _version, _access, _signature, normalize(_superName), normalizedInterfaces);
406                 }
407             }
408         }
409 
410         @Override
411         public AnnotationVisitor visitAnnotation (String desc, boolean visible)
412         {
413             MyAnnotationVisitor visitor = new MyAnnotationVisitor(normalize(desc), new ArrayList<Value>())
414             {
415                 @Override
416                 public void visitEnd()
417                 {
418                     super.visitEnd();
419 
420                     //call all AnnotationHandlers with classname, annotation name + values
421                     for (Handler h : AnnotationParser.this._handlers)
422                     {
423                         if (h instanceof DiscoverableAnnotationHandler)
424                         {
425                             DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
426                             if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
427                                 dah.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
428                         }
429                     }
430                 }
431             };
432 
433             return visitor;
434         }
435 
436         @Override
437         public MethodVisitor visitMethod (final int access,
438                                           final String name,
439                                           final String methodDesc,
440                                           final String signature,
441                                           final String[] exceptions)
442         {
443 
444             return new EmptyVisitor ()
445             {
446                 @Override
447                 public AnnotationVisitor visitAnnotation(String desc, boolean visible)
448                 {
449                     MyAnnotationVisitor visitor = new MyAnnotationVisitor (normalize(desc), new ArrayList<Value>())
450                     {
451                         @Override
452                         public void visitEnd()
453                         {
454                             super.visitEnd();
455                             //call all AnnotationHandlers with classname, method, annotation name + values
456                             for (Handler h : AnnotationParser.this._handlers)
457                             {
458                                 if (h instanceof DiscoverableAnnotationHandler)
459                                 {
460                                     DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
461                                     if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
462                                         dah.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues);
463                                 }
464                             }
465                         }
466                     };
467 
468                     return visitor;
469                 }
470             };
471         }
472 
473         @Override
474         public FieldVisitor visitField (final int access,
475                                         final String fieldName,
476                                         final String fieldType,
477                                         final String signature,
478                                         final Object value)
479         {
480 
481             return new EmptyVisitor ()
482             {
483                 @Override
484                 public AnnotationVisitor visitAnnotation(String desc, boolean visible)
485                 {
486                     MyAnnotationVisitor visitor = new MyAnnotationVisitor(normalize(desc), new ArrayList<Value>())
487                     {
488                         @Override
489                         public void visitEnd()
490                         {
491                             super.visitEnd();
492                             for (Handler h : AnnotationParser.this._handlers)
493                             {
494                                 if (h instanceof DiscoverableAnnotationHandler)
495                                 {
496                                     DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
497                                     if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
498                                         dah.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
499                                 }
500                             }
501                         }
502                     };
503                     return visitor;
504                 }
505             };
506         }
507     }
508 
509 
510     /**
511      * Register a handler that will be called back when the named annotation is
512      * encountered on a class.
513      *
514      * @deprecated see {@link #registerHandler(Handler)}
515      * @param annotationName
516      * @param handler
517      */
518     @Deprecated
519     public void registerAnnotationHandler (String annotationName, DiscoverableAnnotationHandler handler)
520     {
521         _handlers.add(handler);
522     }
523 
524     
525     /**
526      * @deprecated no replacement provided
527      * @param annotationName
528      */
529     @Deprecated
530     public List<DiscoverableAnnotationHandler> getAnnotationHandlers(String annotationName)
531     {
532         List<DiscoverableAnnotationHandler> handlers = new ArrayList<DiscoverableAnnotationHandler>();
533         for (Handler h:_handlers)
534         {
535             if (h instanceof DiscoverableAnnotationHandler)
536             {
537                 DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
538                 if (annotationName.equals(dah.getAnnotationName()))
539                     handlers.add(dah);
540             }
541         }
542  
543         return handlers;
544     }
545 
546     /**
547      * @deprecated no replacement available
548      */
549     @Deprecated
550     public List<DiscoverableAnnotationHandler> getAnnotationHandlers()
551     {
552         List<DiscoverableAnnotationHandler> allAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
553         for (Handler h:_handlers)
554         {
555             if (h instanceof DiscoverableAnnotationHandler)
556             allAnnotationHandlers.add((DiscoverableAnnotationHandler)h);
557         }
558         return allAnnotationHandlers;
559     }
560 
561     /**
562      * @deprecated see {@link #registerHandler(Handler)}
563      * @param handler
564      */
565     @Deprecated
566     public void registerClassHandler (ClassHandler handler)
567     {
568         _handlers.add(handler);
569     }
570     
571     
572     
573     /**
574      * Add a particular handler
575      * 
576      * @param h
577      */
578     public void registerHandler(Handler h)
579     {
580         if (h == null)
581             return;
582         
583         _handlers.add(h);
584     }
585     
586     
587     /**
588      * Add a list of handlers
589      * 
590      * @param handlers
591      */
592     public void registerHandlers(List<? extends Handler> handlers)
593     {
594         if (handlers == null)
595             return;
596         _handlers.addAll(handlers);
597     }
598     
599     
600     /**
601      * Remove a particular handler
602      * 
603      * @param h
604      */
605     public boolean deregisterHandler(Handler h)
606     {
607         return _handlers.remove(h);
608     }
609     
610     
611     /**
612      * Remove all registered handlers
613      */
614     public void clearHandlers()
615     {
616         _handlers.clear();
617     }
618     
619 
620     /**
621      * True if the class has already been processed, false otherwise
622      * @param className
623      */
624     public boolean isParsed (String className)
625     {
626         return _parsedClassNames.contains(className);
627     }
628 
629     
630     
631     /**
632      * Parse a given class
633      * 
634      * @param className
635      * @param resolver
636      * @throws Exception
637      */
638     public void parse (String className, ClassNameResolver resolver)
639     throws Exception
640     {
641         if (className == null)
642             return;
643 
644         if (!resolver.isExcluded(className))
645         {
646             if (!isParsed(className) || resolver.shouldOverride(className))
647             {
648                 className = className.replace('.', '/')+".class";
649                 URL resource = Loader.getResource(this.getClass(), className);
650                 if (resource!= null)
651                 {
652                     Resource r = Resource.newResource(resource);
653                     scanClass(r.getInputStream());
654                 }
655             }
656         }
657     }
658 
659     
660     
661     /**
662      * Parse the given class, optionally walking its inheritance hierarchy
663      * 
664      * @param clazz
665      * @param resolver
666      * @param visitSuperClasses
667      * @throws Exception
668      */
669     public void parse (Class<?> clazz, ClassNameResolver resolver, boolean visitSuperClasses)
670     throws Exception
671     {
672         Class<?> cz = clazz;
673         while (cz != null)
674         {
675             if (!resolver.isExcluded(cz.getName()))
676             {
677                 if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName()))
678                 {
679                     String nameAsResource = cz.getName().replace('.', '/')+".class";
680                     URL resource = Loader.getResource(this.getClass(), nameAsResource);
681                     if (resource!= null)
682                     {
683                         Resource r = Resource.newResource(resource);
684                         scanClass(r.getInputStream());
685                     }
686                 }
687             }
688             if (visitSuperClasses)
689                 cz = cz.getSuperclass();
690             else
691                 cz = null;
692         }
693     }
694 
695     
696     
697     /**
698      * Parse the given classes
699      * 
700      * @param classNames
701      * @param resolver
702      * @throws Exception
703      */
704     public void parse (String[] classNames, ClassNameResolver resolver)
705     throws Exception
706     {
707         if (classNames == null)
708             return;
709 
710         parse(Arrays.asList(classNames), resolver);
711     }
712 
713     
714     /**
715      * Parse the given classes
716      * 
717      * @param classNames
718      * @param resolver
719      * @throws Exception
720      */
721     public void parse (List<String> classNames, ClassNameResolver resolver)
722     throws Exception
723     {
724         for (String s:classNames)
725         {
726             if ((resolver == null) || (!resolver.isExcluded(s) &&  (!isParsed(s) || resolver.shouldOverride(s))))
727             {
728                 s = s.replace('.', '/')+".class";
729                 URL resource = Loader.getResource(this.getClass(), s);
730                 if (resource!= null)
731                 {
732                     Resource r = Resource.newResource(resource);
733                     scanClass(r.getInputStream());
734                 }
735             }
736         }
737     }
738 
739     
740     /**
741      * Parse all classes in a directory
742      * 
743      * @param dir
744      * @param resolver
745      * @throws Exception
746      */
747     public void parseDir (Resource dir, ClassNameResolver resolver)
748     throws Exception
749     {
750         //skip dirs whose name start with . (ie hidden)
751         if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith("."))
752             return;
753 
754         if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
755 
756         String[] files=dir.list();
757         for (int f=0;files!=null && f<files.length;f++)
758         {
759             try
760             {
761                 Resource res = dir.addPath(files[f]);
762                 if (res.isDirectory())
763                     parseDir(res, resolver);
764                 else
765                 {
766                     //we've already verified the directories, so just verify the class file name
767                     String filename = res.getFile().getName();
768                     if (isValidClassFileName(filename))
769                     {
770                         String name = res.getName();
771                         if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
772                         {
773                             Resource r = Resource.newResource(res.getURL());
774                             if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
775                             scanClass(r.getInputStream());
776                         }
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                     parseJarEntry(jarUri, entry, resolver);
816                 }
817                 catch (Exception e)
818                 {
819                     LOG.warn("Problem parsing jar entry: {}", entry.getName());
820                 }
821             }
822 
823         };
824 
825         scanner.scan(null, loader, nullInclusive, visitParents);
826     }
827 
828 
829     /**
830      * Parse classes in the supplied uris.
831      * 
832      * @param uris
833      * @param resolver
834      * @throws Exception
835      */
836     public void parse (URI[] uris, final ClassNameResolver resolver)
837     throws Exception
838     {
839         if (uris==null)
840             return;
841 
842         for (URI uri:uris)
843         {
844             try
845             {
846                 parse(uri, resolver);
847             }
848             catch (Exception e)
849             {
850                 LOG.warn("Problem parsing classes from {}", uri);
851             }
852         }
853 
854     }
855 
856     /**
857      * Parse a particular uri
858      * @param uri
859      * @param resolver
860      * @throws Exception
861      */
862     public void parse (URI uri, final ClassNameResolver resolver)
863     throws Exception
864     {
865         if (uri == null)
866             return;
867 
868         parse (Resource.newResource(uri), resolver);
869         
870      
871     }
872 
873     
874     /**
875      * Parse a resource
876      * @param r
877      * @param resolver
878      * @throws Exception
879      */
880     public void parse (Resource r, final ClassNameResolver resolver)
881     throws Exception
882     {
883         if (r == null)
884             return;
885         
886         if (r.exists() && r.isDirectory())
887         {
888             parseDir(r, resolver);
889             return;
890         }
891 
892         String fullname = r.toString();
893         if (fullname.endsWith(".jar"))
894         {
895             parseJar(r, resolver);
896             return;
897         }
898 
899         if (fullname.endsWith(".class"))
900         {
901             scanClass(r.getInputStream());
902             return;
903         }
904         
905         if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", r);
906     }
907 
908 
909     /**
910      * Parse a resource that is a jar file.
911      * 
912      * @param jarResource
913      * @param resolver
914      * @throws Exception
915      */
916     public void parseJar (Resource jarResource,  final ClassNameResolver resolver)
917     throws Exception
918     {
919         if (jarResource == null)
920             return;
921         
922         URI uri = jarResource.getURI();
923         if (jarResource.toString().endsWith(".jar"))
924         {
925             if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);};
926             
927             //treat it as a jar that we need to open and scan all entries from             
928             InputStream in = jarResource.getInputStream();
929             if (in==null)
930                 return;
931 
932             JarInputStream jar_in = new JarInputStream(in);
933             try
934             { 
935                 JarEntry entry = jar_in.getNextJarEntry();
936                 while (entry!=null)
937                 {      
938                     parseJarEntry(uri, entry, resolver);
939                     entry = jar_in.getNextJarEntry();
940                 }
941             }
942             finally
943             {
944                 jar_in.close();
945             } 
946         }   
947     }
948 
949     /**
950      * Parse a single entry in a jar file
951      * @param jar
952      * @param entry
953      * @param resolver
954      * @throws Exception
955      */
956     protected void parseJarEntry (URI jar, JarEntry entry, final ClassNameResolver resolver)
957     throws Exception
958     {
959         if (jar == null || entry == null)
960             return;
961 
962         //skip directories
963         if (entry.isDirectory())
964             return;
965 
966         String name = entry.getName();
967 
968         //check file is a valid class file name
969         if (isValidClassFileName(name) && isValidClassFilePath(name))
970         {
971             String shortName =  name.replace('/', '.').substring(0,name.length()-6);
972 
973             if ((resolver == null)
974                     ||
975                 (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
976             {
977                 Resource clazz = Resource.newResource("jar:"+jar+"!/"+name);
978                 if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
979                 scanClass(clazz.getInputStream());
980             }
981         }
982     }
983     
984     
985 
986     /**
987      * Use ASM on a class
988      * 
989      * @param is
990      * @throws IOException
991      */
992     protected void scanClass (InputStream is)
993     throws IOException
994     {
995         ClassReader reader = new ClassReader(is);
996         reader.accept(new MyClassVisitor(), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
997     }
998     
999     /**
1000      * Check that the given path represents a valid class file name.
1001      * The check is fairly cursory, checking that:
1002      * <ul>
1003      * <li> the name ends with .class</li>
1004      * <li> it isn't a dot file or in a hidden directory </li>
1005      * <li> the name of the class at least begins with a valid identifier for a class name </li>
1006      * </ul>
1007      * @param name
1008      * @return
1009      */
1010     private boolean isValidClassFileName (String name)
1011     {
1012         //no name cannot be valid
1013         if (name == null || name.length()==0)
1014             return false;
1015 
1016         //skip anything that is not a class file
1017         if (!name.toLowerCase(Locale.ENGLISH).endsWith(".class"))
1018         {
1019             if (LOG.isDebugEnabled()) LOG.debug("Not a class: {}",name);
1020             return false;
1021         }
1022 
1023         //skip any classfiles that are not a valid java identifier
1024         int c0 = 0;      
1025         int ldir = name.lastIndexOf('/', name.length()-6);
1026         c0 = (ldir > -1 ? ldir+1 : c0);
1027         if (!Character.isJavaIdentifierStart(name.charAt(c0)))
1028         {
1029             if (LOG.isDebugEnabled()) LOG.debug("Not a java identifier: {}"+name);
1030             return false;
1031         }
1032    
1033         return true;
1034     }
1035     
1036     
1037     /**
1038      * Check that the given path does not contain hidden directories
1039      *
1040      * @param path
1041      * @return
1042      */
1043     private boolean isValidClassFilePath (String path)
1044     {
1045         //no path is not valid
1046         if (path == null || path.length()==0)
1047             return false;
1048 
1049         //skip any classfiles that are in a hidden directory
1050         if (path.startsWith(".") || path.contains("/."))
1051         {
1052             if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: {}"+path);
1053             return false;
1054         }
1055 
1056         return true;
1057     }
1058 }
1059