1
2
3
4
5
6
7 package net.sf.gumshoe.indexer;
8
9 import java.io.File;
10 import java.io.FilenameFilter;
11 import java.io.IOException;
12 import java.lang.reflect.Modifier;
13 import java.util.ArrayList;
14 import java.util.Enumeration;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Set;
19 import java.util.StringTokenizer;
20 import java.util.zip.ZipFile;
21
22 /***
23 * @author Gabor
24 *
25 * TODO To change the template for this generated type comment go to Window -
26 * Preferences - Java - Code Style - Code Templates
27 */
28 public class FindClasses {
29
30 /***
31 *
32 */
33 private FindClasses() {
34 super();
35
36 }
37
38 /***
39 * Convenience method that finds classes on the standard java classpath
40 *
41 * @param superClass
42 * @return a List of classes extending superClass
43 * @throws IOException
44 * @throws ClassNotFoundException
45 */
46 public static List findClassesThatExtend(Class superClass)
47 throws IOException, ClassNotFoundException {
48 String javaClassPath = System.getProperty("java.class.path");
49 String paths[] = javaClassPath.split("" + File.pathSeparatorChar);
50 Class superClasses[] = new Class[1];
51 superClasses[0] = superClass;
52 return findClassesThatExtend(paths, superClasses);
53 }
54
55 /***
56 * Convenience method for <code>findClassesThatExtend(Class[],
57 * boolean)</code>
58 * with the option to include inner classes in the search set to false.
59 *
60 * @return ArrayList containing discovered classes.
61 */
62 public static List findClassesThatExtend(String[] paths,
63 Class[] superClasses) throws IOException, ClassNotFoundException {
64 return findClassesThatExtend(paths, superClasses, false);
65 }
66
67 public static List findClassesThatExtend(String[] strPathsOrJars,
68 Class[] superClasses, boolean innerClasses) throws IOException,
69 ClassNotFoundException {
70 List listPaths = null;
71 ArrayList listClasses = null;
72 List listSuperClasses = null;
73 strPathsOrJars = addJarsInPath(strPathsOrJars);
74 listPaths = getClasspathMatches(strPathsOrJars);
75 listClasses = new ArrayList();
76 listSuperClasses = new ArrayList();
77 for (int i = 0; i < superClasses.length; i++) {
78 listSuperClasses.add(superClasses[i].getName());
79 }
80
81 findClassesInPaths(listPaths, listClasses);
82 List subClassList = findAllSubclasses(listSuperClasses, listClasses,
83 innerClasses);
84 return subClassList;
85 }
86
87 /***
88 * Find classes in the provided path(s)/jar(s) that extend the class(es).
89 *
90 * @return ArrayList containing discovered classes
91 */
92 private static String[] addJarsInPath(String[] paths) {
93 Set fullList = new HashSet();
94 for (int i = 0; i < paths.length; i++) {
95 fullList.add(paths[i]);
96 if (!paths[i].endsWith(".jar")) {
97 File dir = new File(paths[i]);
98 if (dir.exists() && dir.isDirectory()) {
99 String[] jars = dir.list(new FilenameFilter() {
100 public boolean accept(File f, String name) {
101 if (name.endsWith(".jar")) {
102 return true;
103 }
104 return false;
105 }
106 });
107 for (int x = 0; x < jars.length; x++) {
108 fullList.add(jars[x]);
109 }
110 }
111 }
112 }
113 return (String[]) fullList.toArray(new String[0]);
114 }
115
116 private static List getClasspathMatches(String[] strPathsOrJars) {
117 ArrayList listPaths = null;
118 StringTokenizer stPaths = null;
119 String strPath = null;
120 int i;
121 listPaths = new ArrayList();
122
123 stPaths = new StringTokenizer(System.getProperty("java.class.path"),
124 System.getProperty("path.separator"));
125 if (strPathsOrJars != null) {
126 strPathsOrJars = fixDotDirs(strPathsOrJars);
127 strPathsOrJars = fixSlashes(strPathsOrJars);
128 strPathsOrJars = fixEndingSlashes(strPathsOrJars);
129 }
130
131
132
133
134
135 while (stPaths.hasMoreTokens()) {
136 strPath = fixDotDir((String) stPaths.nextToken());
137 strPath = fixSlashes(strPath);
138 strPath = fixEndingSlashes(strPath);
139 if (strPathsOrJars == null) {
140
141 listPaths.add(strPath);
142 } else {
143 boolean found = false;
144 for (i = 0; i < strPathsOrJars.length; i++) {
145 if (strPath.endsWith(strPathsOrJars[i])) {
146 found = true;
147
148 listPaths.add(strPath);
149 break;
150 }
151 }
152 if (!found) {
153
154 }
155 }
156 }
157 return listPaths;
158 }
159
160 private static void findClassesInPaths(List listPaths, List listClasses)
161 throws IOException {
162 Iterator iterPaths = listPaths.iterator();
163 while (iterPaths.hasNext()) {
164 findClassesInOnePath((String) iterPaths.next(), listClasses);
165 }
166 }
167
168 /***
169 * Finds all classes that extend the class, searching in the listAllClasses
170 * ArrayList.
171 *
172 * @param theClass
173 * the parent class
174 * @param listAllClasses
175 * the collection of classes to search in
176 * @param listSubClasses
177 * the collection of discovered subclasses
178 * @param innerClasses
179 * indicates whether inners classes should be included in the
180 * search
181 */
182 private static void findAllSubclassesOneClass(Class theClass,
183 List listAllClasses, List listSubClasses, boolean innerClasses) {
184 Iterator iterClasses = null;
185 String strClassName = null;
186 Class c = null;
187 boolean bIsSubclass = false;
188 iterClasses = listAllClasses.iterator();
189 while (iterClasses.hasNext()) {
190 strClassName = (String) iterClasses.next();
191
192
193 if ((strClassName.indexOf("$") == -1) || innerClasses) {
194
195 try {
196 c = Class.forName(strClassName, false, Thread
197 .currentThread().getContextClassLoader());
198
199 if (!c.isInterface()
200 && !Modifier.isAbstract(c.getModifiers())) {
201 bIsSubclass = theClass.isAssignableFrom(c);
202 } else {
203 bIsSubclass = false;
204 }
205 if (bIsSubclass) {
206 listSubClasses.add(strClassName);
207 }
208 } catch (Throwable ignored) {
209 }
210 }
211 }
212 }
213
214 /***
215 * Finds all classes that extend the classes in the listSuperClasses
216 * ArrayList, searching in the listAllClasses ArrayList.
217 *
218 * @param listSuperClasses the base classes to find subclasses for
219 * @param listAllClasses the collection of classes to search in
220 * @param innerClasses indicate whether to include inner classes in
221 * the search
222 *@return ArrayList of the subclasses
223 */
224 private static ArrayList findAllSubclasses(
225 List listSuperClasses,
226 List listAllClasses,
227 boolean innerClasses)
228 {
229 Iterator iterClasses = null;
230 ArrayList listSubClasses = null;
231 String strClassName = null;
232 Class tempClass = null;
233 listSubClasses = new ArrayList();
234 iterClasses = listSuperClasses.iterator();
235 while (iterClasses.hasNext())
236 {
237 strClassName = (String) iterClasses.next();
238
239
240 if ((strClassName.indexOf("$") == -1) || innerClasses)
241 {
242
243 try
244 {
245 tempClass =
246 Class.forName(
247 strClassName,
248 false,
249 Thread.currentThread().getContextClassLoader());
250 findAllSubclassesOneClass(
251 tempClass,
252 listAllClasses,
253 listSubClasses,
254 innerClasses);
255
256 }
257 catch (Throwable ignored)
258 {
259 }
260 }
261 }
262 return listSubClasses;
263 }
264
265 private static void findClassesInOnePath(String strPath, List listClasses)
266 throws IOException {
267 File file = null;
268 ZipFile zipFile = null;
269 Enumeration entries = null;
270 String strEntry = null;
271 file = new File(strPath);
272 if (file.isDirectory()) {
273 findClassesInPathsDir(strPath, file, listClasses);
274 } else if (file.exists()) {
275 zipFile = new ZipFile(file);
276 entries = zipFile.entries();
277 while (entries.hasMoreElements()) {
278 strEntry = entries.nextElement().toString();
279 if (strEntry.endsWith(".class")) {
280 listClasses.add(fixClassName(strEntry));
281 }
282 }
283 }
284 }
285
286 /***
287 * Converts a class file from the text stored in a Jar file to a version
288 * that can be used in Class.forName().
289 *
290 * @param strClassName
291 * the class name from a Jar file
292 * @return String the Java-style dotted version of the name
293 */
294 private static String fixClassName(String strClassName) {
295 strClassName = strClassName.replace('//', '.');
296 strClassName = strClassName.replace('/', '.');
297 strClassName = strClassName.substring(0, strClassName.length() - 6);
298
299 return strClassName;
300 }
301
302 private static void findClassesInPathsDir(String strPathElement, File dir,
303 List listClasses) throws IOException {
304 File file = null;
305 String[] list = dir.list();
306 for (int i = 0; i < list.length; i++) {
307 file = new File(dir, list[i]);
308 if (file.isDirectory()) {
309 findClassesInPathsDir(strPathElement, file, listClasses);
310 } else if (file.exists() && (file.length() != 0)
311 && list[i].endsWith(".class")) {
312 listClasses.add(file.getPath().substring(
313 strPathElement.length() + 1,
314 file.getPath().lastIndexOf(".")).replace(
315 File.separator.charAt(0), '.'));
316 }
317 }
318 }
319
320 private static String fixDotDir(String path) {
321 if (path == null)
322 return null;
323 if (path.equals(".")) {
324 return System.getProperty("user.dir");
325 } else {
326 return path.trim();
327 }
328 }
329
330 private static String[] fixDotDirs(String[] paths)
331 {
332 for (int i = 0; i < paths.length; i++)
333 {
334 paths[i] = fixDotDir(paths[i]);
335 }
336 return paths;
337 }
338
339 private static String[] fixEndingSlashes(String[] strings) {
340 String[] strNew = new String[strings.length];
341 for (int i = 0; i < strings.length; i++) {
342 strNew[i] = fixEndingSlashes(strings[i]);
343 }
344 return strNew;
345 }
346
347 private static String fixEndingSlashes(String string) {
348 if (string.endsWith("/") || string.endsWith("//")) {
349 string = string.substring(0, string.length() - 1);
350 string = fixEndingSlashes(string);
351 }
352 return string;
353 }
354
355 private static String[] fixSlashes(String[] strings) {
356 String[] strNew = new String[strings.length];
357 for (int i = 0; i < strings.length; i++) {
358 strNew[i] = fixSlashes(strings[i])
359 }
360 return strNew;
361 }
362
363 private static String fixSlashes(String str) {
364
365 str = str.replace('//', '/');
366
367
368
369 str = replaceString(str, "//", "_____");
370 str = replaceString(str, "_____", "/");
371 return str;
372 }
373
374 private static String replaceString(String s, String strToFind,
375 String strToReplace) {
376 int index;
377 int currentPos;
378 StringBuffer buffer = null;
379 if (s.indexOf(strToFind) == -1) {
380 return s;
381 }
382 currentPos = 0;
383 buffer = new StringBuffer();
384 while (true) {
385 index = s.indexOf(strToFind, currentPos);
386 if (index == -1) {
387 break;
388 }
389 buffer.append(s.substring(currentPos, index));
390 buffer.append(strToReplace);
391 currentPos = index + strToFind.length();
392 }
393 buffer.append(s.substring(currentPos));
394 return buffer.toString();
395 }
396 }