1 | /* |
---|
2 | * This program is free software; you can redistribute it and/or modify |
---|
3 | * it under the terms of the GNU General Public License as published by |
---|
4 | * the Free Software Foundation; either version 2 of the License, or |
---|
5 | * (at your option) any later version. |
---|
6 | * |
---|
7 | * This program is distributed in the hope that it will be useful, |
---|
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
10 | * GNU General Public License for more details. |
---|
11 | * |
---|
12 | * You should have received a copy of the GNU General Public License |
---|
13 | * along with this program; if not, write to the Free Software |
---|
14 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
15 | */ |
---|
16 | |
---|
17 | /* |
---|
18 | * ClassDiscovery.java |
---|
19 | * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand |
---|
20 | * |
---|
21 | */ |
---|
22 | |
---|
23 | package weka.core; |
---|
24 | |
---|
25 | import java.io.File; |
---|
26 | import java.lang.reflect.Modifier; |
---|
27 | import java.net.URISyntaxException; |
---|
28 | import java.net.URL; |
---|
29 | import java.net.URLClassLoader; |
---|
30 | import java.util.Collections; |
---|
31 | import java.util.Comparator; |
---|
32 | import java.util.Enumeration; |
---|
33 | import java.util.HashSet; |
---|
34 | import java.util.Hashtable; |
---|
35 | import java.util.StringTokenizer; |
---|
36 | import java.util.Vector; |
---|
37 | import java.util.jar.JarEntry; |
---|
38 | import java.util.jar.JarFile; |
---|
39 | |
---|
40 | /** |
---|
41 | * This class is used for discovering classes that implement a certain |
---|
42 | * interface or a derived from a certain class. |
---|
43 | * |
---|
44 | * @author FracPete (fracpete at waikato dot ac dot nz) |
---|
45 | * @version $Revision: 6074 $ |
---|
46 | * @see StringCompare |
---|
47 | */ |
---|
48 | public class ClassDiscovery |
---|
49 | implements RevisionHandler { |
---|
50 | |
---|
51 | /** whether to output some debug information. */ |
---|
52 | public final static boolean VERBOSE = false; |
---|
53 | |
---|
54 | /** for caching queries (classname+packagename <-> Vector with classnames). */ |
---|
55 | protected static Hashtable<String,Vector<String>> m_Cache; |
---|
56 | |
---|
57 | /** notify if VERBOSE is still on */ |
---|
58 | static { |
---|
59 | if (VERBOSE) |
---|
60 | System.err.println(ClassDiscovery.class.getName() + ": VERBOSE ON"); |
---|
61 | } |
---|
62 | |
---|
63 | /** |
---|
64 | * Checks whether the "otherclass" is a subclass of the given "superclass". |
---|
65 | * |
---|
66 | * @param superclass the superclass to check against |
---|
67 | * @param otherclass this class is checked whether it is a subclass |
---|
68 | * of the the superclass |
---|
69 | * @return TRUE if "otherclass" is a true subclass |
---|
70 | */ |
---|
71 | public static boolean isSubclass(String superclass, String otherclass) { |
---|
72 | try { |
---|
73 | return isSubclass(Class.forName(superclass), Class.forName(otherclass)); |
---|
74 | } |
---|
75 | catch (Exception e) { |
---|
76 | return false; |
---|
77 | } |
---|
78 | } |
---|
79 | |
---|
80 | /** |
---|
81 | * Checks whether the "otherclass" is a subclass of the given "superclass". |
---|
82 | * |
---|
83 | * @param superclass the superclass to check against |
---|
84 | * @param otherclass this class is checked whether it is a subclass |
---|
85 | * of the the superclass |
---|
86 | * @return TRUE if "otherclass" is a true subclass |
---|
87 | */ |
---|
88 | public static boolean isSubclass(Class superclass, Class otherclass) { |
---|
89 | Class currentclass; |
---|
90 | boolean result; |
---|
91 | |
---|
92 | result = false; |
---|
93 | currentclass = otherclass; |
---|
94 | do { |
---|
95 | result = currentclass.equals(superclass); |
---|
96 | |
---|
97 | // topmost class reached? |
---|
98 | if (currentclass.equals(Object.class)) |
---|
99 | break; |
---|
100 | |
---|
101 | if (!result) |
---|
102 | currentclass = currentclass.getSuperclass(); |
---|
103 | } |
---|
104 | while (!result); |
---|
105 | |
---|
106 | return result; |
---|
107 | } |
---|
108 | |
---|
109 | /** |
---|
110 | * Checks whether the given class implements the given interface. |
---|
111 | * |
---|
112 | * @param intf the interface to look for in the given class |
---|
113 | * @param cls the class to check for the interface |
---|
114 | * @return TRUE if the class contains the interface |
---|
115 | */ |
---|
116 | public static boolean hasInterface(String intf, String cls) { |
---|
117 | try { |
---|
118 | return hasInterface(Class.forName(intf), Class.forName(cls)); |
---|
119 | } |
---|
120 | catch (Exception e) { |
---|
121 | return false; |
---|
122 | } |
---|
123 | } |
---|
124 | |
---|
125 | /** |
---|
126 | * Checks whether the given class implements the given interface. |
---|
127 | * |
---|
128 | * @param intf the interface to look for in the given class |
---|
129 | * @param cls the class to check for the interface |
---|
130 | * @return TRUE if the class contains the interface |
---|
131 | */ |
---|
132 | public static boolean hasInterface(Class intf, Class cls) { |
---|
133 | Class[] intfs; |
---|
134 | int i; |
---|
135 | boolean result; |
---|
136 | Class currentclass; |
---|
137 | |
---|
138 | result = false; |
---|
139 | currentclass = cls; |
---|
140 | do { |
---|
141 | // check all the interfaces, this class implements |
---|
142 | intfs = currentclass.getInterfaces(); |
---|
143 | for (i = 0; i < intfs.length; i++) { |
---|
144 | if (intfs[i].equals(intf)) { |
---|
145 | result = true; |
---|
146 | break; |
---|
147 | } |
---|
148 | } |
---|
149 | |
---|
150 | // get parent class |
---|
151 | if (!result) { |
---|
152 | currentclass = currentclass.getSuperclass(); |
---|
153 | |
---|
154 | // topmost class reached or no superclass? |
---|
155 | if ( (currentclass == null) || (currentclass.equals(Object.class)) ) |
---|
156 | break; |
---|
157 | } |
---|
158 | } |
---|
159 | while (!result); |
---|
160 | |
---|
161 | return result; |
---|
162 | } |
---|
163 | |
---|
164 | /** |
---|
165 | * If the given package can be found in this part of the classpath then |
---|
166 | * an URL object is returned, otherwise <code>null</code>. |
---|
167 | * |
---|
168 | * @param classpathPart the part of the classpath to look for the package |
---|
169 | * @param pkgname the package to look for |
---|
170 | * @return if found, the url as string, otherwise null |
---|
171 | */ |
---|
172 | protected static URL getURL(String classpathPart, String pkgname) { |
---|
173 | String urlStr; |
---|
174 | URL result; |
---|
175 | File classpathFile; |
---|
176 | File file; |
---|
177 | JarFile jarfile; |
---|
178 | Enumeration enm; |
---|
179 | String pkgnameTmp; |
---|
180 | |
---|
181 | result = null; |
---|
182 | urlStr = null; |
---|
183 | |
---|
184 | try { |
---|
185 | classpathFile = new File(classpathPart); |
---|
186 | |
---|
187 | // directory or jar? |
---|
188 | if (classpathFile.isDirectory()) { |
---|
189 | // does the package exist in this directory? |
---|
190 | file = new File(classpathPart + pkgname); |
---|
191 | if (file.exists()) |
---|
192 | urlStr = "file:" + classpathPart + pkgname; |
---|
193 | } |
---|
194 | else { |
---|
195 | // is package actually included in jar? |
---|
196 | jarfile = new JarFile(classpathPart); |
---|
197 | enm = jarfile.entries(); |
---|
198 | pkgnameTmp = pkgname.substring(1); // remove the leading "/" |
---|
199 | while (enm.hasMoreElements()) { |
---|
200 | if (enm.nextElement().toString().startsWith(pkgnameTmp)) { |
---|
201 | urlStr = "jar:file:" + classpathPart + "!" + pkgname; |
---|
202 | break; |
---|
203 | } |
---|
204 | } |
---|
205 | } |
---|
206 | } |
---|
207 | catch (Exception e) { |
---|
208 | // ignore |
---|
209 | } |
---|
210 | |
---|
211 | // try to generate URL from url string |
---|
212 | if (urlStr != null) { |
---|
213 | try { |
---|
214 | result = new URL(urlStr); |
---|
215 | } |
---|
216 | catch (Exception e) { |
---|
217 | System.err.println( |
---|
218 | "Trying to create URL from '" + urlStr |
---|
219 | + "' generates this exception:\n" + e); |
---|
220 | result = null; |
---|
221 | } |
---|
222 | } |
---|
223 | |
---|
224 | return result; |
---|
225 | } |
---|
226 | |
---|
227 | /** |
---|
228 | * Checks the given packages for classes that inherited from the given class, |
---|
229 | * in case it's a class, or implement this class, in case it's an interface. |
---|
230 | * |
---|
231 | * @param classname the class/interface to look for |
---|
232 | * @param pkgnames the packages to search in |
---|
233 | * @return a list with all the found classnames |
---|
234 | */ |
---|
235 | public static Vector<String> find(String classname, String[] pkgnames) { |
---|
236 | Vector<String> result; |
---|
237 | Class cls; |
---|
238 | |
---|
239 | result = new Vector<String>(); |
---|
240 | |
---|
241 | try { |
---|
242 | cls = Class.forName(classname); |
---|
243 | result = find(cls, pkgnames); |
---|
244 | } |
---|
245 | catch (Exception e) { |
---|
246 | e.printStackTrace(); |
---|
247 | } |
---|
248 | |
---|
249 | return result; |
---|
250 | } |
---|
251 | |
---|
252 | /** |
---|
253 | * Checks the given package for classes that inherited from the given class, |
---|
254 | * in case it's a class, or implement this class, in case it's an interface. |
---|
255 | * |
---|
256 | * @param classname the class/interface to look for |
---|
257 | * @param pkgname the package to search in |
---|
258 | * @return a list with all the found classnames |
---|
259 | */ |
---|
260 | public static Vector<String> find(String classname, String pkgname) { |
---|
261 | Vector<String> result; |
---|
262 | Class cls; |
---|
263 | |
---|
264 | result = new Vector<String>(); |
---|
265 | |
---|
266 | try { |
---|
267 | cls = Class.forName(classname); |
---|
268 | result = find(cls, pkgname); |
---|
269 | } |
---|
270 | catch (Exception e) { |
---|
271 | e.printStackTrace(); |
---|
272 | } |
---|
273 | |
---|
274 | return result; |
---|
275 | } |
---|
276 | |
---|
277 | /** |
---|
278 | * Checks the given packages for classes that inherited from the given class, |
---|
279 | * in case it's a class, or implement this class, in case it's an interface. |
---|
280 | * |
---|
281 | * @param cls the class/interface to look for |
---|
282 | * @param pkgnames the packages to search in |
---|
283 | * @return a list with all the found classnames |
---|
284 | */ |
---|
285 | public static Vector<String> find(Class cls, String[] pkgnames) { |
---|
286 | Vector<String> result; |
---|
287 | int i; |
---|
288 | HashSet<String> names; |
---|
289 | |
---|
290 | result = new Vector<String>(); |
---|
291 | |
---|
292 | names = new HashSet<String>(); |
---|
293 | for (i = 0; i < pkgnames.length; i++) |
---|
294 | names.addAll(find(cls, pkgnames[i])); |
---|
295 | |
---|
296 | // sort result |
---|
297 | result.addAll(names); |
---|
298 | Collections.sort(result, new StringCompare()); |
---|
299 | |
---|
300 | return result; |
---|
301 | } |
---|
302 | |
---|
303 | /** |
---|
304 | * Checks the given package for classes that inherited from the given class, |
---|
305 | * in case it's a class, or implement this class, in case it's an interface. |
---|
306 | * |
---|
307 | * @param cls the class/interface to look for |
---|
308 | * @param pkgname the package to search in |
---|
309 | * @return a list with all the found classnames |
---|
310 | */ |
---|
311 | public static Vector<String> find(Class cls, String pkgname) { |
---|
312 | Vector<String> result; |
---|
313 | StringTokenizer tok; |
---|
314 | String part; |
---|
315 | String pkgpath; |
---|
316 | File dir; |
---|
317 | File[] files; |
---|
318 | URL url; |
---|
319 | int i; |
---|
320 | Class clsNew; |
---|
321 | String classname; |
---|
322 | JarFile jar; |
---|
323 | JarEntry entry; |
---|
324 | Enumeration enm; |
---|
325 | |
---|
326 | |
---|
327 | // already cached? |
---|
328 | result = getCache(cls, pkgname); |
---|
329 | |
---|
330 | if (result == null) { |
---|
331 | result = new Vector<String>(); |
---|
332 | |
---|
333 | if (VERBOSE) |
---|
334 | System.out.println( |
---|
335 | "Searching for '" + cls.getName() + "' in '" + pkgname + "':"); |
---|
336 | |
---|
337 | // turn package into path |
---|
338 | pkgpath = pkgname.replaceAll("\\.", "/"); |
---|
339 | |
---|
340 | // check all parts of the classpath, to include additional classes from |
---|
341 | // "parallel" directories/jars, not just the first occurence |
---|
342 | /*tok = new StringTokenizer( |
---|
343 | System.getProperty("java.class.path"), |
---|
344 | System.getProperty("path.separator")); */ |
---|
345 | |
---|
346 | ClassloaderUtil clu = new ClassloaderUtil(); |
---|
347 | URLClassLoader sysLoader = (URLClassLoader)clu.getClass().getClassLoader(); |
---|
348 | URL[] cl_urls = sysLoader.getURLs(); |
---|
349 | |
---|
350 | //while (tok.hasMoreTokens()) { |
---|
351 | for (i = 0; i < cl_urls.length; i++) { |
---|
352 | //part = tok.nextToken(); |
---|
353 | part = cl_urls[i].toString(); |
---|
354 | if (part.startsWith("file:")) { |
---|
355 | part.replace(" ", "%20"); |
---|
356 | try { |
---|
357 | File temp = new File(new java.net.URI(part)); |
---|
358 | part = temp.getAbsolutePath(); |
---|
359 | } catch (URISyntaxException e) { |
---|
360 | e.printStackTrace(); |
---|
361 | } |
---|
362 | } |
---|
363 | if (VERBOSE) |
---|
364 | System.out.println("Classpath-part: " + part); |
---|
365 | |
---|
366 | // does package exist in this part of the classpath? |
---|
367 | url = getURL(part, "/" + pkgpath); |
---|
368 | if (VERBOSE) { |
---|
369 | if (url == null) |
---|
370 | System.out.println(" " + pkgpath + " NOT FOUND"); |
---|
371 | else |
---|
372 | System.out.println(" " + pkgpath + " FOUND"); |
---|
373 | } |
---|
374 | if (url == null) |
---|
375 | continue; |
---|
376 | |
---|
377 | // find classes |
---|
378 | dir = new File(part + "/" + pkgpath); |
---|
379 | if (dir.exists()) { |
---|
380 | files = dir.listFiles(); |
---|
381 | for (int j = 0; j < files.length; j++) { |
---|
382 | // only class files |
---|
383 | if ( (!files[j].isFile()) |
---|
384 | || (!files[j].getName().endsWith(".class")) ) |
---|
385 | continue; |
---|
386 | |
---|
387 | try { |
---|
388 | classname = pkgname + "." |
---|
389 | + files[j].getName().replaceAll(".*/", "") |
---|
390 | .replaceAll("\\.class", ""); |
---|
391 | result.add(classname); |
---|
392 | } |
---|
393 | catch (Exception e) { |
---|
394 | e.printStackTrace(); |
---|
395 | } |
---|
396 | } |
---|
397 | } |
---|
398 | else { |
---|
399 | try { |
---|
400 | jar = new JarFile(part); |
---|
401 | enm = jar.entries(); |
---|
402 | while (enm.hasMoreElements()) { |
---|
403 | entry = (JarEntry) enm.nextElement(); |
---|
404 | |
---|
405 | // only class files |
---|
406 | if ( (entry.isDirectory()) |
---|
407 | || (!entry.getName().endsWith(".class")) ) |
---|
408 | continue; |
---|
409 | |
---|
410 | classname = entry.getName().replaceAll("\\.class", ""); |
---|
411 | |
---|
412 | // only classes in the particular package |
---|
413 | if (!classname.startsWith(pkgpath)) |
---|
414 | continue; |
---|
415 | |
---|
416 | // no sub-package |
---|
417 | if (classname.substring(pkgpath.length() + 1).indexOf("/") > -1) |
---|
418 | continue; |
---|
419 | |
---|
420 | result.add(classname.replaceAll("/", ".")); |
---|
421 | } |
---|
422 | } |
---|
423 | catch (Exception e) { |
---|
424 | e.printStackTrace(); |
---|
425 | } |
---|
426 | } |
---|
427 | } |
---|
428 | |
---|
429 | // check classes |
---|
430 | i = 0; |
---|
431 | while (i < result.size()) { |
---|
432 | try { |
---|
433 | clsNew = Class.forName((String) result.get(i)); |
---|
434 | |
---|
435 | // no abstract classes |
---|
436 | if (Modifier.isAbstract(clsNew.getModifiers())) |
---|
437 | result.remove(i); |
---|
438 | // must implement interface |
---|
439 | else if ( (cls.isInterface()) && (!hasInterface(cls, clsNew)) ) |
---|
440 | result.remove(i); |
---|
441 | // must be derived from class |
---|
442 | else if ( (!cls.isInterface()) && (!isSubclass(cls, clsNew)) ) |
---|
443 | result.remove(i); |
---|
444 | else |
---|
445 | i++; |
---|
446 | } |
---|
447 | catch (Throwable e) { |
---|
448 | System.err.println("Checking class: " + result.get(i)); |
---|
449 | e.printStackTrace(); |
---|
450 | result.remove(i); |
---|
451 | } |
---|
452 | } |
---|
453 | |
---|
454 | // sort result |
---|
455 | Collections.sort(result, new StringCompare()); |
---|
456 | |
---|
457 | // add to cache |
---|
458 | addCache(cls, pkgname, result); |
---|
459 | } |
---|
460 | |
---|
461 | return result; |
---|
462 | } |
---|
463 | |
---|
464 | /** |
---|
465 | * adds all the sub-directories recursively to the list. |
---|
466 | * |
---|
467 | * @param prefix the path prefix |
---|
468 | * @param dir the directory to look in for sub-dirs |
---|
469 | * @param list the current list of sub-dirs |
---|
470 | * @return the new list of sub-dirs |
---|
471 | */ |
---|
472 | protected static HashSet<String> getSubDirectories(String prefix, File dir, HashSet<String> list) { |
---|
473 | File[] files; |
---|
474 | int i; |
---|
475 | String newPrefix; |
---|
476 | |
---|
477 | // add directory to the list |
---|
478 | if (prefix == null) |
---|
479 | newPrefix = ""; |
---|
480 | else if (prefix.length() == 0) |
---|
481 | newPrefix = dir.getName(); |
---|
482 | else |
---|
483 | newPrefix = prefix + "." + dir.getName(); |
---|
484 | |
---|
485 | if (newPrefix.length() != 0) |
---|
486 | list.add(newPrefix); |
---|
487 | |
---|
488 | // search for sub-directories |
---|
489 | files = dir.listFiles(); |
---|
490 | if (files != null) { |
---|
491 | for (i = 0; i < files.length; i++) { |
---|
492 | if (files[i].isDirectory()) |
---|
493 | list = getSubDirectories(newPrefix, files[i], list); |
---|
494 | } |
---|
495 | } |
---|
496 | |
---|
497 | return list; |
---|
498 | } |
---|
499 | |
---|
500 | /** |
---|
501 | * Lists all packages it can find in the classpath. |
---|
502 | * |
---|
503 | * @return a list with all the found packages |
---|
504 | */ |
---|
505 | public static Vector<String> findPackages() { |
---|
506 | Vector<String> result; |
---|
507 | StringTokenizer tok; |
---|
508 | String part; |
---|
509 | File file; |
---|
510 | JarFile jar; |
---|
511 | JarEntry entry; |
---|
512 | Enumeration<JarEntry> enm; |
---|
513 | HashSet<String> set; |
---|
514 | |
---|
515 | result = new Vector<String>(); |
---|
516 | set = new HashSet<String>(); |
---|
517 | |
---|
518 | // check all parts of the classpath, to include additional classes from |
---|
519 | // "parallel" directories/jars, not just the first occurence |
---|
520 | tok = new StringTokenizer( |
---|
521 | System.getProperty("java.class.path"), |
---|
522 | System.getProperty("path.separator")); |
---|
523 | |
---|
524 | while (tok.hasMoreTokens()) { |
---|
525 | part = tok.nextToken(); |
---|
526 | if (VERBOSE) |
---|
527 | System.out.println("Classpath-part: " + part); |
---|
528 | |
---|
529 | // find classes |
---|
530 | file = new File(part); |
---|
531 | if (file.isDirectory()) { |
---|
532 | set = getSubDirectories(null, file, set); |
---|
533 | } |
---|
534 | else if (file.exists()) { |
---|
535 | try { |
---|
536 | jar = new JarFile(part); |
---|
537 | enm = jar.entries(); |
---|
538 | while (enm.hasMoreElements()) { |
---|
539 | entry = (JarEntry) enm.nextElement(); |
---|
540 | |
---|
541 | // only directories |
---|
542 | if (entry.isDirectory()) |
---|
543 | set.add(entry.getName().replaceAll("/", ".").replaceAll("\\.$", "")); |
---|
544 | } |
---|
545 | } |
---|
546 | catch (Exception e) { |
---|
547 | e.printStackTrace(); |
---|
548 | } |
---|
549 | } |
---|
550 | } |
---|
551 | |
---|
552 | // sort result |
---|
553 | set.remove("META-INF"); |
---|
554 | result.addAll(set); |
---|
555 | Collections.sort(result, new StringCompare()); |
---|
556 | |
---|
557 | return result; |
---|
558 | } |
---|
559 | |
---|
560 | /** |
---|
561 | * initializes the cache for the classnames. |
---|
562 | */ |
---|
563 | protected static void initCache() { |
---|
564 | if (m_Cache == null) |
---|
565 | m_Cache = new Hashtable<String,Vector<String>>(); |
---|
566 | } |
---|
567 | |
---|
568 | /** |
---|
569 | * adds the list of classnames to the cache. |
---|
570 | * |
---|
571 | * @param cls the class to cache the classnames for |
---|
572 | * @param pkgname the package name the classes were found in |
---|
573 | * @param classnames the list of classnames to cache |
---|
574 | */ |
---|
575 | protected static void addCache(Class cls, String pkgname, Vector<String> classnames) { |
---|
576 | initCache(); |
---|
577 | m_Cache.put(cls.getName() + "-" + pkgname, classnames); |
---|
578 | } |
---|
579 | |
---|
580 | /** |
---|
581 | * returns the list of classnames associated with this class and package, if |
---|
582 | * available, otherwise null. |
---|
583 | * |
---|
584 | * @param cls the class to get the classnames for |
---|
585 | * @param pkgname the package name for the classes |
---|
586 | * @return the classnames if found, otherwise null |
---|
587 | */ |
---|
588 | protected static Vector<String> getCache(Class cls, String pkgname) { |
---|
589 | initCache(); |
---|
590 | return m_Cache.get(cls.getName() + "-" + pkgname); |
---|
591 | } |
---|
592 | |
---|
593 | /** |
---|
594 | * clears the cache for class/classnames relation. |
---|
595 | */ |
---|
596 | public static void clearCache() { |
---|
597 | initCache(); |
---|
598 | m_Cache.clear(); |
---|
599 | } |
---|
600 | |
---|
601 | /** |
---|
602 | * Returns the revision string. |
---|
603 | * |
---|
604 | * @return the revision |
---|
605 | */ |
---|
606 | public String getRevision() { |
---|
607 | return RevisionUtils.extract("$Revision: 6074 $"); |
---|
608 | } |
---|
609 | |
---|
610 | /** |
---|
611 | * Possible calls: |
---|
612 | * <ul> |
---|
613 | * <li> |
---|
614 | * weka.core.ClassDiscovery <packages><br/> |
---|
615 | * Prints all the packages in the current classpath |
---|
616 | * </li> |
---|
617 | * <li> |
---|
618 | * weka.core.ClassDiscovery <classname> <packagename(s)><br/> |
---|
619 | * Prints the classes it found. |
---|
620 | * </li> |
---|
621 | * </ul> |
---|
622 | * |
---|
623 | * @param args the commandline arguments |
---|
624 | */ |
---|
625 | public static void main(String[] args) { |
---|
626 | Vector<String> list; |
---|
627 | Vector<String> packages; |
---|
628 | int i; |
---|
629 | StringTokenizer tok; |
---|
630 | |
---|
631 | if ((args.length == 1) && (args[0].equals("packages"))) { |
---|
632 | list = findPackages(); |
---|
633 | for (i = 0; i < list.size(); i++) |
---|
634 | System.out.println(list.get(i)); |
---|
635 | } |
---|
636 | else if (args.length == 2) { |
---|
637 | // packages |
---|
638 | packages = new Vector<String>(); |
---|
639 | tok = new StringTokenizer(args[1], ","); |
---|
640 | while (tok.hasMoreTokens()) |
---|
641 | packages.add(tok.nextToken()); |
---|
642 | |
---|
643 | // search |
---|
644 | list = ClassDiscovery.find( |
---|
645 | args[0], |
---|
646 | (String[]) packages.toArray(new String[packages.size()])); |
---|
647 | |
---|
648 | // print result, if any |
---|
649 | System.out.println( |
---|
650 | "Searching for '" + args[0] + "' in '" + args[1] + "':\n" |
---|
651 | + " " + list.size() + " found."); |
---|
652 | for (i = 0; i < list.size(); i++) |
---|
653 | System.out.println(" " + (i+1) + ". " + list.get(i)); |
---|
654 | } |
---|
655 | else { |
---|
656 | System.out.println("\nUsage:"); |
---|
657 | System.out.println( |
---|
658 | ClassDiscovery.class.getName() + " packages"); |
---|
659 | System.out.println("\tlists all packages in the classpath"); |
---|
660 | System.out.println( |
---|
661 | ClassDiscovery.class.getName() + " <classname> <packagename(s)>"); |
---|
662 | System.out.println("\tlists classes derived from/implementing 'classname' that"); |
---|
663 | System.out.println("\tcan be found in 'packagename(s)' (comma-separated list"); |
---|
664 | System.out.println(); |
---|
665 | System.exit(1); |
---|
666 | } |
---|
667 | } |
---|
668 | |
---|
669 | /** |
---|
670 | * compares two strings. The following order is used:<br/> |
---|
671 | * <ul> |
---|
672 | * <li>case insensitive</li> |
---|
673 | * <li>german umlauts (ä , ö etc.) or other non-ASCII letters |
---|
674 | * are treated as special chars</li> |
---|
675 | * <li>special chars < numbers < letters</li> |
---|
676 | * </ul> |
---|
677 | */ |
---|
678 | public static class StringCompare |
---|
679 | implements Comparator, RevisionHandler { |
---|
680 | |
---|
681 | /** |
---|
682 | * appends blanks to the string if its shorter than <code>len</code>. |
---|
683 | * |
---|
684 | * @param s the string to pad |
---|
685 | * @param len the minimum length for the string to have |
---|
686 | * @return the padded string |
---|
687 | */ |
---|
688 | private String fillUp(String s, int len) { |
---|
689 | while (s.length() < len) |
---|
690 | s += " "; |
---|
691 | return s; |
---|
692 | } |
---|
693 | |
---|
694 | /** |
---|
695 | * returns the group of the character: 0=special char, 1=number, 2=letter. |
---|
696 | * |
---|
697 | * @param c the character to check |
---|
698 | * @return the group |
---|
699 | */ |
---|
700 | private int charGroup(char c) { |
---|
701 | int result; |
---|
702 | |
---|
703 | result = 0; |
---|
704 | |
---|
705 | if ( (c >= 'a') && (c <= 'z') ) |
---|
706 | result = 2; |
---|
707 | else if ( (c >= '0') && (c <= '9') ) |
---|
708 | result = 1; |
---|
709 | |
---|
710 | return result; |
---|
711 | } |
---|
712 | |
---|
713 | /** |
---|
714 | * Compares its two arguments for order. |
---|
715 | * |
---|
716 | * @param o1 the first object |
---|
717 | * @param o2 the second object |
---|
718 | * @return -1 if o1<o2, 0 if o1=o2 and 1 if o1&;gt;o2 |
---|
719 | */ |
---|
720 | public int compare(Object o1, Object o2) { |
---|
721 | String s1; |
---|
722 | String s2; |
---|
723 | int i; |
---|
724 | int result; |
---|
725 | int v1; |
---|
726 | int v2; |
---|
727 | |
---|
728 | result = 0; // they're equal |
---|
729 | |
---|
730 | // get lower case string |
---|
731 | s1 = o1.toString().toLowerCase(); |
---|
732 | s2 = o2.toString().toLowerCase(); |
---|
733 | |
---|
734 | // same length |
---|
735 | s1 = fillUp(s1, s2.length()); |
---|
736 | s2 = fillUp(s2, s1.length()); |
---|
737 | |
---|
738 | for (i = 0; i < s1.length(); i++) { |
---|
739 | // same char? |
---|
740 | if (s1.charAt(i) == s2.charAt(i)) { |
---|
741 | result = 0; |
---|
742 | } |
---|
743 | else { |
---|
744 | v1 = charGroup(s1.charAt(i)); |
---|
745 | v2 = charGroup(s2.charAt(i)); |
---|
746 | |
---|
747 | // different type (special, number, letter)? |
---|
748 | if (v1 != v2) { |
---|
749 | if (v1 < v2) |
---|
750 | result = -1; |
---|
751 | else |
---|
752 | result = 1; |
---|
753 | } |
---|
754 | else { |
---|
755 | if (s1.charAt(i) < s2.charAt(i)) |
---|
756 | result = -1; |
---|
757 | else |
---|
758 | result = 1; |
---|
759 | } |
---|
760 | |
---|
761 | break; |
---|
762 | } |
---|
763 | } |
---|
764 | |
---|
765 | return result; |
---|
766 | } |
---|
767 | |
---|
768 | /** |
---|
769 | * Indicates whether some other object is "equal to" this Comparator. |
---|
770 | * |
---|
771 | * @param obj the object to compare with this Comparator |
---|
772 | * @return true if the object is a StringCompare object as well |
---|
773 | */ |
---|
774 | public boolean equals(Object obj) { |
---|
775 | return (obj instanceof StringCompare); |
---|
776 | } |
---|
777 | |
---|
778 | /** |
---|
779 | * Returns the revision string. |
---|
780 | * |
---|
781 | * @return the revision |
---|
782 | */ |
---|
783 | public String getRevision() { |
---|
784 | return RevisionUtils.extract("$Revision: 6074 $"); |
---|
785 | } |
---|
786 | } |
---|
787 | } |
---|