View Javadoc

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