KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jorphan > reflect > ClassFinder


1 // $Header: /home/cvs/jakarta-jmeter/src/jorphan/org/apache/jorphan/reflect/ClassFinder.java,v 1.9.2.1 2004/09/18 20:30:59 sebb Exp $
2
/*
3  * Copyright 2001-2004 The Apache Software Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17 */

18
19 package org.apache.jorphan.reflect;
20
21 import java.io.File JavaDoc;
22 import java.io.FilenameFilter JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.lang.reflect.Modifier JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Enumeration JavaDoc;
27 import java.util.HashSet JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Set JavaDoc;
31 import java.util.StringTokenizer JavaDoc;
32 import java.util.TreeSet JavaDoc;
33 import java.util.zip.ZipFile JavaDoc;
34
35 import org.apache.jorphan.logging.LoggingManager;
36 import org.apache.log.Logger;
37
38 /**
39  * This class finds classes that implement one or more specified interfaces.
40  *
41  * @author Burt Beckwith
42  * @author Michael Stover (mstover1 at apache.org)
43  * @version $Revision: 1.9.2.1 $
44  */

45 public final class ClassFinder
46 {
47     transient private static Logger log = LoggingManager.getLoggerForClass();
48     private ClassFinder()
49     {
50     }
51
52     // static only
53
/**
54      * Convenience method for <code>findClassesThatExtend(Class[],
55      * boolean)</code> with the option to include inner classes in the search
56      * set to false.
57      *
58      * @return ArrayList containing discovered classes.
59      */

60     public static List JavaDoc findClassesThatExtend(
61         String JavaDoc[] paths,
62         Class JavaDoc[] superClasses)
63         throws IOException JavaDoc, ClassNotFoundException JavaDoc
64     {
65         return findClassesThatExtend(paths, superClasses, false);
66     }
67
68     /**
69      * Find classes in the provided path(s)/jar(s) that extend the class(es).
70      *
71      * @return ArrayList containing discovered classes
72      */

73     private static String JavaDoc[] addJarsInPath(String JavaDoc[] paths)
74     {
75         Set JavaDoc fullList = new HashSet JavaDoc();
76         for (int i = 0; i < paths.length; i++)
77         {
78             fullList.add(paths[i]);
79             if (!paths[i].endsWith(".jar"))
80             {
81                 File JavaDoc dir = new File JavaDoc(paths[i]);
82                 if (dir.exists() && dir.isDirectory())
83                 {
84                     String JavaDoc[] jars = dir.list(new FilenameFilter JavaDoc()
85                     {
86                         public boolean accept(File JavaDoc f, String JavaDoc name)
87                         {
88                             if (name.endsWith(".jar"))
89                             {
90                                 return true;
91                             }
92                             return false;
93                         }
94                     });
95                     for (int x = 0; x < jars.length; x++)
96                     {
97                         fullList.add(jars[x]);
98                     }
99                 }
100             }
101         }
102         return (String JavaDoc[]) fullList.toArray(new String JavaDoc[0]);
103     }
104     public static List JavaDoc findClassesThatExtend(
105         String JavaDoc[] strPathsOrJars,
106         Class JavaDoc[] superClasses,
107         boolean innerClasses)
108         throws IOException JavaDoc, ClassNotFoundException JavaDoc
109     {
110         List JavaDoc listPaths = null;
111         Set JavaDoc listClasses = null;
112         List JavaDoc listSuperClasses = null;
113         strPathsOrJars = addJarsInPath(strPathsOrJars);
114         if (log.isDebugEnabled())
115         {
116             for (int k = 0; k < strPathsOrJars.length; k++)
117             {
118                 log.debug("strPathsOrJars : " + strPathsOrJars[k]);
119             }
120         }
121         listPaths = getClasspathMatches(strPathsOrJars);
122         if (log.isDebugEnabled())
123         {
124             Iterator JavaDoc tIter = listPaths.iterator();
125             for (; tIter.hasNext();)
126             {
127                 log.debug("listPaths : " + tIter.next());
128             }
129         }
130         listClasses = new TreeSet JavaDoc();
131         listSuperClasses = new ArrayList JavaDoc();
132         for (int i = 0; i < superClasses.length; i++)
133         {
134             listSuperClasses.add(superClasses[i].getName());
135         }
136         // first get all the classes
137
findClassesInPaths(listPaths, listClasses);
138         if (log.isDebugEnabled())
139         {
140             Iterator JavaDoc tIter = listClasses.iterator();
141             for (; tIter.hasNext();)
142             {
143                 log.debug("listClasses : " + tIter.next());
144             }
145         }
146         Set JavaDoc subClassList =
147             findAllSubclasses(listSuperClasses, listClasses, innerClasses);
148         return new ArrayList JavaDoc(subClassList);
149     }
150
151     private static List JavaDoc getClasspathMatches(String JavaDoc[] strPathsOrJars)
152     {
153         ArrayList JavaDoc listPaths = null;
154         StringTokenizer JavaDoc stPaths = null;
155         String JavaDoc strPath = null;
156         int i;
157         listPaths = new ArrayList JavaDoc();
158         log.debug("Classpath = " + System.getProperty("java.class.path"));
159         stPaths =
160             new StringTokenizer JavaDoc(
161                 System.getProperty("java.class.path"),
162                 System.getProperty("path.separator"));
163         if (strPathsOrJars != null)
164         {
165             strPathsOrJars = fixDotDirs(strPathsOrJars);
166             strPathsOrJars = fixSlashes(strPathsOrJars);
167             strPathsOrJars = fixEndingSlashes(strPathsOrJars);
168         }
169         if (log.isDebugEnabled())
170         {
171             for (i = 0; i < strPathsOrJars.length; i++)
172             {
173                 log.debug("strPathsOrJars[" + i + "] : " + strPathsOrJars[i]);
174             }
175         }
176         
177         // find all jar files or paths that end with strPathOrJar
178
while (stPaths.hasMoreTokens())
179         {
180             strPath = fixDotDir((String JavaDoc) stPaths.nextToken());
181             strPath = fixSlashes(strPath);
182             strPath = fixEndingSlashes(strPath);
183             if (strPathsOrJars == null)
184             {
185                 log.debug("Adding: " + strPath);
186                 listPaths.add(strPath);
187             }
188             else
189             {
190                 boolean found=false;
191                 for (i = 0; i < strPathsOrJars.length; i++)
192                 {
193                     if (strPath.endsWith(strPathsOrJars[i]))
194                     {
195                         found=true;
196                         log.debug("Adding "+strPath+" found at "+i);
197                         listPaths.add(strPath);
198                         break;// no need to look further
199
}
200                 }
201                 if (!found){
202                     log.debug("Did not find: "+strPath);
203                 }
204             }
205         }
206         return listPaths;
207     }
208     
209     /**
210      * Get all interfaces that the class implements, including parent
211      * interfaces. This keeps us from having to instantiate and check
212      * instanceof, which wouldn't work anyway since instanceof requires a
213      * hard-coded class or interface name.
214      *
215      * @param theClass the class to get interfaces for
216      * @param hInterfaces a Map to store the discovered interfaces in
217      *
218      * NOTUSED
219     private static void getAllInterfaces(Class theClass, Map hInterfaces)
220     {
221         Class[] interfaces = theClass.getInterfaces();
222         for (int i = 0; i < interfaces.length; i++)
223         {
224             hInterfaces.put(interfaces[i].getName(), interfaces[i]);
225             getAllInterfaces(interfaces[i], hInterfaces);
226         }
227     }
228     */

229     private static String JavaDoc[] fixDotDirs(String JavaDoc[] paths)
230     {
231         for (int i = 0; i < paths.length; i++)
232         {
233             paths[i] = fixDotDir(paths[i]);
234         }
235         return paths;
236     }
237     private static String JavaDoc fixDotDir(String JavaDoc path)
238     {
239         if (path == null) return null;
240         if (path.equals("."))
241         {
242             return System.getProperty("user.dir");
243         }
244         else
245         {
246             return path.trim();
247         }
248     }
249     private static String JavaDoc[] fixEndingSlashes(String JavaDoc[] strings)
250     {
251         String JavaDoc[] strNew = new String JavaDoc[strings.length];
252         for (int i = 0; i < strings.length; i++)
253         {
254             strNew[i] = fixEndingSlashes(strings[i]);
255         }
256         return strNew;
257     }
258     private static String JavaDoc fixEndingSlashes(String JavaDoc string)
259     {
260         if (string.endsWith("/") || string.endsWith("\\"))
261         {
262             string = string.substring(0, string.length() - 1);
263             string = fixEndingSlashes(string);
264         }
265         return string;
266     }
267     private static String JavaDoc[] fixSlashes(String JavaDoc[] strings)
268     {
269         String JavaDoc[] strNew = new String JavaDoc[strings.length];
270         for (int i = 0; i < strings.length; i++)
271         {
272             strNew[i] = fixSlashes(strings[i]) /*.toLowerCase()*/;
273         }
274         return strNew;
275     }
276     private static String JavaDoc fixSlashes(String JavaDoc str)
277     {
278         // replace \ with /
279
str = str.replace('\\', '/');
280         // compress multiples into singles;
281
// do in 2 steps with dummy string
282
// to avoid infinte loop
283
str = replaceString(str, "//", "_____");
284         str = replaceString(str, "_____", "/");
285         return str;
286     }
287     private static String JavaDoc replaceString(
288         String JavaDoc s,
289         String JavaDoc strToFind,
290         String JavaDoc strToReplace)
291     {
292         int index;
293         int currentPos;
294         StringBuffer JavaDoc buffer = null;
295         if (s.indexOf(strToFind) == -1)
296         {
297             return s;
298         }
299         currentPos = 0;
300         buffer = new StringBuffer JavaDoc();
301         while (true)
302         {
303             index = s.indexOf(strToFind, currentPos);
304             if (index == -1)
305             {
306                 break;
307             }
308             buffer.append(s.substring(currentPos, index));
309             buffer.append(strToReplace);
310             currentPos = index + strToFind.length();
311         }
312         buffer.append(s.substring(currentPos));
313         return buffer.toString();
314     }
315     
316     /**
317      * NOTUSED
318      * * Determine if the class implements the interface.
319      *
320      * @param theClass the class to check
321      * @param theInterface the interface to look for
322      * @return boolean true if it implements
323      *
324     private static boolean classImplementsInterface(
325         Class theClass,
326         Class theInterface)
327     {
328         HashMap mapInterfaces = new HashMap();
329         String strKey = null;
330         // pass in the map by reference since the method is recursive
331         getAllInterfaces(theClass, mapInterfaces);
332         Iterator iterInterfaces = mapInterfaces.keySet().iterator();
333         while (iterInterfaces.hasNext())
334         {
335             strKey = (String) iterInterfaces.next();
336             if (mapInterfaces.get(strKey) == theInterface)
337             {
338                 return true;
339             }
340         }
341         return false;
342     }
343     */

344     
345     /**
346      * Convenience method for <code>findAllSubclasses(List, List,
347      * boolean)</code> with the option to include inner classes in the search
348      * set to false.
349      *
350      * @param listSuperClasses the base classes to find subclasses for
351      * @param listAllClasses the collection of classes to search in
352      * @return ArrayList of the subclasses
353      *
354      * NOTUSED
355     private static ArrayList findAllSubclasses(
356         List listSuperClasses,
357         List listAllClasses)
358     {
359         return findAllSubclasses(listSuperClasses, listAllClasses, false);
360     }
361     */

362     
363     /**
364      * Finds all classes that extend the classes in the listSuperClasses
365      * ArrayList, searching in the listAllClasses ArrayList.
366      *
367      * @param listSuperClasses the base classes to find subclasses for
368      * @param listAllClasses the collection of classes to search in
369      * @param innerClasses indicate whether to include inner classes in
370      * the search
371      *@return ArrayList of the subclasses
372      */

373     private static Set JavaDoc findAllSubclasses(
374         List JavaDoc listSuperClasses,
375         Set JavaDoc listAllClasses,
376         boolean innerClasses)
377     {
378         Iterator JavaDoc iterClasses = null;
379         Set JavaDoc listSubClasses = null;
380         String JavaDoc strClassName = null;
381         Class JavaDoc tempClass = null;
382         listSubClasses = new TreeSet JavaDoc();
383         iterClasses = listSuperClasses.iterator();
384         while (iterClasses.hasNext())
385         {
386             strClassName = (String JavaDoc) iterClasses.next();
387             // only check classes if they are not inner classes
388
// or we intend to check for inner classes
389
if ((strClassName.indexOf("$") == -1) || innerClasses)
390             {
391                 // might throw an exception, assume this is ignorable
392
try
393                 {
394                     tempClass =
395                         Class.forName(
396                             strClassName,
397                             false,
398                             Thread.currentThread().getContextClassLoader());
399                     findAllSubclassesOneClass(
400                         tempClass,
401                         listAllClasses,
402                         listSubClasses,
403                         innerClasses);
404                     // call by reference - recursive
405
}
406                 catch (Throwable JavaDoc ignored)
407                 {
408                 }
409             }
410         }
411         return listSubClasses;
412     }
413     
414     /**
415      * Convenience method for <code>findAllSubclassesOneClass(Class, List, List,
416      * boolean)</code> with option to include inner classes in the search set to
417      * false.
418      *
419      * @param theClass the parent class
420      * @param listAllClasses the collection of classes to search in
421      * @param listSubClasses the collection of discovered subclasses
422      *
423      * NOTUSED
424     private static void findAllSubclassesOneClass(
425         Class theClass,
426         List listAllClasses,
427         List listSubClasses)
428     {
429         findAllSubclassesOneClass(
430             theClass,
431             listAllClasses,
432             listSubClasses,
433             false);
434     }
435     */

436     
437     /**
438      * Finds all classes that extend the class, searching in the listAllClasses
439      * ArrayList.
440      *
441      * @param theClass the parent class
442      * @param listAllClasses the collection of classes to search in
443      * @param listSubClasses the collection of discovered subclasses
444      * @param innerClasses indicates whether inners classes should be
445      * included in the search
446      */

447     private static void findAllSubclassesOneClass(
448         Class JavaDoc theClass,
449         Set JavaDoc listAllClasses,
450         Set JavaDoc listSubClasses,
451         boolean innerClasses)
452     {
453         Iterator JavaDoc iterClasses = null;
454         String JavaDoc strClassName = null;
455         Class JavaDoc c = null;
456         boolean bIsSubclass = false;
457         iterClasses = listAllClasses.iterator();
458         while (iterClasses.hasNext())
459         {
460             strClassName = (String JavaDoc) iterClasses.next();
461             // only check classes if they are not inner classes
462
// or we intend to check for inner classes
463
if ((strClassName.indexOf("$") == -1) || innerClasses)
464             {
465                 // might throw an exception, assume this is ignorable
466
try
467                 {
468                     c =
469                         Class.forName(
470                             strClassName,
471                             false,
472                             Thread.currentThread().getContextClassLoader());
473
474                     if (!c.isInterface()
475                         && !Modifier.isAbstract(c.getModifiers()))
476                     {
477                         bIsSubclass = theClass.isAssignableFrom(c);
478                     }
479                     else
480                     {
481                         bIsSubclass = false;
482                     }
483                     if (bIsSubclass)
484                     {
485                         listSubClasses.add(strClassName);
486                     }
487                 }
488                 catch (Throwable JavaDoc ignored)
489                 {
490                 }
491             }
492         }
493     }
494     
495     /**
496      * Converts a class file from the text stored in a Jar file to a version
497      * that can be used in Class.forName().
498      *
499      * @param strClassName the class name from a Jar file
500      * @return String the Java-style dotted version of the name
501      */

502     private static String JavaDoc fixClassName(String JavaDoc strClassName)
503     {
504         strClassName = strClassName.replace('\\', '.');
505         strClassName = strClassName.replace('/', '.');
506         strClassName = strClassName.substring(0, strClassName.length() - 6);
507         // remove ".class"
508
return strClassName;
509     }
510     
511     private static void findClassesInOnePath(String JavaDoc strPath, Set JavaDoc listClasses)
512         throws IOException JavaDoc
513     {
514         File JavaDoc file = null;
515         ZipFile JavaDoc zipFile = null;
516         Enumeration JavaDoc entries = null;
517         String JavaDoc strEntry = null;
518         file = new File JavaDoc(strPath);
519         if (file.isDirectory())
520         {
521             findClassesInPathsDir(strPath, file, listClasses);
522         }
523         else if (file.exists())
524         {
525             zipFile = new ZipFile JavaDoc(file);
526             entries = zipFile.entries();
527             while (entries.hasMoreElements())
528             {
529                 strEntry = entries.nextElement().toString();
530                 if (strEntry.endsWith(".class"))
531                 {
532                     listClasses.add(fixClassName(strEntry));
533                 }
534             }
535         }
536     }
537
538     private static void findClassesInPaths(List JavaDoc listPaths, Set JavaDoc listClasses)
539         throws IOException JavaDoc
540     {
541         Iterator JavaDoc iterPaths = listPaths.iterator();
542         while (iterPaths.hasNext())
543         {
544             findClassesInOnePath((String JavaDoc) iterPaths.next(), listClasses);
545         }
546     }
547
548     private static void findClassesInPathsDir(
549         String JavaDoc strPathElement,
550         File JavaDoc dir,
551         Set JavaDoc listClasses)
552         throws IOException JavaDoc
553     {
554         File JavaDoc file = null;
555         String JavaDoc[] list = dir.list();
556         for (int i = 0; i < list.length; i++)
557         {
558             file = new File JavaDoc(dir, list[i]);
559             if (file.isDirectory())
560             {
561                 findClassesInPathsDir(strPathElement, file, listClasses);
562             }
563             else if (
564                 file.exists()
565                     && (file.length() != 0)
566                     && list[i].endsWith(".class"))
567             {
568                 listClasses.add(
569                     file
570                         .getPath()
571                         .substring(
572                             strPathElement.length() + 1,
573                             file.getPath().lastIndexOf("."))
574                         .replace(File.separator.charAt(0), '.'));
575             }
576         }
577     }
578 }
Popular Tags