Java tutorial
//package com.webex.ta.hydra.util; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; import java.util.zip.ZipFile; /** * Created by Cisco WebEx. User: vegaz Date: 2010-8-30 Time: 14:33:33 */ public final class ClassFinder { private static final String DOT_JAR = ".jar"; // $NON-NLS-1$ private static final String DOT_CLASS = ".class"; // $NON-NLS-1$ private static final int DOT_CLASS_LEN = DOT_CLASS.length(); // static only private ClassFinder() { } /** * Filter updates to TreeSet by only storing classes that extend one of the * parent classes * * */ private static class FilterTreeSet extends TreeSet { private final Class[] parents; // parent classes to check private final boolean inner; // are inner classes OK? private final transient ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); // Potentially // expensive; do it // once FilterTreeSet(Class[] parents, boolean inner) { super(); this.parents = parents; this.inner = inner; } /** * Override the superclass so we only add classnames that meet the * criteria. * * @param o * - classname (must be a String) * @return true if it is a new entry * * @see java.util.TreeSet#add(java.lang.Object) */ public boolean add(Object o) { if (contains(o)) return false;// No need to check it again String s = (String) o;// we only expect Strings if ((s.indexOf("$") == -1) || inner) { // $NON-NLS-1$ if (isChildOf(parents, s, contextClassLoader)) { return super.add(s); } } return false; } } public static List<String> findClassesThatExtend(String path, Class[] superClasses) throws IOException { String[] paths = new String[1]; paths[0] = path; return findClassesThatExtend(paths, superClasses); } public static List<String> findClassesThatExtend(String[] paths, Class[] superClasses) throws IOException { return findClassesThatExtend(paths, superClasses, false); } // For each directory in the search path, add all the jars found there private static String[] addJarsInPath(String[] paths) { Set<String> fullList = new HashSet<String>(); for (final String path : paths) { fullList.add(path); // Keep the unexpanded path // TODO - allow directories to end with .jar by removing this check? if (!path.endsWith(DOT_JAR)) { File dir = new File(path); if (dir.exists() && dir.isDirectory()) { String[] jars = dir.list(new FilenameFilter() { public boolean accept(File f, String name) { return name.endsWith(DOT_JAR); } }); fullList.addAll(Arrays.asList(jars)); } } } return fullList.toArray(new String[0]); } public static List<String> findClassesThatExtend(String[] strPathsOrJars, final Class[] superClasses, final boolean innerClasses) throws IOException { // Find all jars in the search path // strPathsOrJars = addJarsInPath(strPathsOrJars); for (int k = 0; k < strPathsOrJars.length; k++) { strPathsOrJars[k] = fixPathEntry(strPathsOrJars[k]); } // Now eliminate any classpath entries that do not "match" the search // List listPaths = getClasspathMatches(strPathsOrJars); List<String> listPaths = new ArrayList<String>(); for (String path : strPathsOrJars) { listPaths.add(path); } Set<String> listClasses = new FilterTreeSet(superClasses, innerClasses); // first get all the classes findClassesInPaths(listPaths, listClasses); // // Now keep only the required classes // Set subClassList = findAllSubclasses(superClasses, listClasses, // innerClasses); // if (log.isDebugEnabled()) { // log.debug("subClassList.size()="+subClassList.size()); // Iterator tIter = subClassList.iterator(); // while (tIter.hasNext()) { // log.debug("subClassList : " + tIter.next()); // } // } return new ArrayList<String>(listClasses);// subClassList); } /* * Returns the classpath entries that match the search list of jars and * paths */ private static List getClasspathMatches(String[] strPathsOrJars) { StringTokenizer classPaths = new StringTokenizer(System.getProperty("java.class.path"), // $NON-NLS-1$ System.getProperty("path.separator")); // $NON-NLS-1$ // find all jar files or paths that end with strPathOrJar ArrayList<String> listPaths = new ArrayList<String>(); String classPath = null; while (classPaths.hasMoreTokens()) { classPath = fixPathEntry(classPaths.nextToken()); if (strPathsOrJars == null) { listPaths.add(classPath); } else { boolean found = false; for (String strPathsOrJar : strPathsOrJars) { if (classPath.endsWith(strPathsOrJar)) { found = true; listPaths.add(classPath); break;// no need to look further } } if (!found) { // log.debug("Did not find: " + strPath); } } } return listPaths; } /** * Fix a path: - replace "." by current directory - trim any trailing spaces * - replace \ by / - replace // by / - remove all trailing / * * @param path * @return path */ private static String fixPathEntry(String path) { if (path == null) return null; if (path.equals(".")) { return System.getProperty("user.dir"); } path = path.trim().replace('\\', '/'); path = substitute(path, "//", "/"); while (path.endsWith("/")) { // $NON-NLS-1$ path = path.substring(0, path.length() - 1); } return path; } /* * NOTUSED * Determine if the class implements the interface. * * @param theClass the class to check * * @param theInterface the interface to look for * * @return boolean true if it implements * * private static boolean classImplementsInterface( Class theClass, Class * theInterface) { HashMap mapInterfaces = new HashMap(); String strKey = * null; // pass in the map by reference since the method is recursive * getAllInterfaces(theClass, mapInterfaces); Iterator iterInterfaces = * mapInterfaces.keySet().iterator(); while (iterInterfaces.hasNext()) { * strKey = (String) iterInterfaces.next(); if (mapInterfaces.get(strKey) == * theInterface) { return true; } } return false; } */ /* * Finds all classes that extend the classes in the listSuperClasses * ArrayList, searching in the listAllClasses ArrayList. * * @param superClasses the base classes to find subclasses for * * @param listAllClasses the collection of classes to search in * * @param innerClasses indicate whether to include inner classes in the * search * * @return ArrayList of the subclasses */ // private static Set findAllSubclasses(Class []superClasses, Set // listAllClasses, boolean innerClasses) { // Set listSubClasses = new TreeSet(); // for (int i=0; i< superClasses.length; i++) { // findAllSubclassesOneClass(superClasses[i], listAllClasses, // listSubClasses, innerClasses); // } // return listSubClasses; // } /* * Finds all classes that extend the class, searching in the listAllClasses * ArrayList. * * @param theClass the parent class * * @param listAllClasses the collection of classes to search in * * @param listSubClasses the collection of discovered subclasses * * @param innerClasses indicates whether inners classes should be included * in the search */ // private static void findAllSubclassesOneClass(Class theClass, Set // listAllClasses, Set listSubClasses, // boolean innerClasses) { // Iterator iterClasses = listAllClasses.iterator(); // while (iterClasses.hasNext()) { // String strClassName = (String) iterClasses.next(); // // only check classes if they are not inner classes // // or we intend to check for inner classes // if ((strClassName.indexOf("$") == -1) || innerClasses) { // $NON-NLS-1$ // // might throw an exception, assume this is ignorable // try { // Class c = Class.forName(strClassName, false, // Thread.currentThread().getContextClassLoader()); // // if (!c.isInterface() && !Modifier.isAbstract(c.getModifiers())) { // if(theClass.isAssignableFrom(c)){ // listSubClasses.add(strClassName); // } // } // } catch (Throwable ignored) { // log.debug(ignored.getLocalizedMessage()); // } // } // } // } /** * * @param parentClasses * list of classes to check for * @param strClassName * name of class to be checked * @param contextClassLoader * the classloader to use * @return */ private static boolean isChildOf(Class[] parentClasses, String strClassName, ClassLoader contextClassLoader) { // might throw an exception, assume this is ignorable try { Class c = Class.forName(strClassName, false, contextClassLoader); if (!c.isInterface() && !Modifier.isAbstract(c.getModifiers())) { for (Class parentClass : parentClasses) { if (parentClass.isAssignableFrom(c)) { return true; } } } } catch (Throwable ignored) { } return false; } /* * Converts a class file from the text stored in a Jar file to a version * that can be used in Class.forName(). * * @param strClassName the class name from a Jar file * * @return String the Java-style dotted version of the name */ private static String fixClassName(String strClassName) { strClassName = strClassName.replace('\\', '.'); // $NON-NLS-1$ // // $NON-NLS-2$ strClassName = strClassName.replace('/', '.'); // $NON-NLS-1$ // // $NON-NLS-2$ // remove ".class" strClassName = strClassName.substring(0, strClassName.length() - DOT_CLASS_LEN); return strClassName; } private static void findClassesInOnePath(String strPath, Set<String> listClasses) throws IOException { File file = new File(strPath); if (file.isDirectory()) { findClassesInPathsDir(strPath, file, listClasses); } else if (file.exists()) { ZipFile zipFile = new ZipFile(file); Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { String strEntry = entries.nextElement().toString(); if (strEntry.endsWith(DOT_CLASS)) { listClasses.add(fixClassName(strEntry)); } } zipFile.close(); } } private static void findClassesInPaths(List listPaths, Set listClasses) throws IOException { for (Object listPath : listPaths) { findClassesInOnePath((String) listPath, listClasses); } } private static void findClassesInPathsDir(String strPathElement, File dir, Set<String> listClasses) throws IOException { String[] list = dir.list(); for (int i = 0; i < list.length; i++) { File file = new File(dir, list[i]); if (file.isDirectory()) { // Recursive call findClassesInPathsDir(strPathElement, file, listClasses); } else if (list[i].endsWith(DOT_CLASS) && file.exists() && (file.length() != 0)) { final String path = file.getPath(); listClasses.add(path.substring(strPathElement.length() + 1, path.lastIndexOf(".")) // $NON-NLS-1$ .replace(File.separator.charAt(0), '.')); // $NON-NLS-1$ } else if (list[i].endsWith(DOT_JAR) && file.exists() && (file.length() != 0)) { ZipFile zipFile = new ZipFile(file); Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { String strEntry = entries.nextElement().toString(); if (strEntry.endsWith(DOT_CLASS)) { listClasses.add(fixClassName(strEntry)); } } zipFile.close(); } } } public static String substitute(final String input, final String pattern, final String sub) { StringBuffer ret = new StringBuffer(input.length()); int start = 0; int index = -1; final int length = pattern.length(); while ((index = input.indexOf(pattern, start)) >= start) { ret.append(input.substring(start, index)); ret.append(sub); start = index + length; } ret.append(input.substring(start)); return ret.toString(); } }