You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jmeter-dev@jakarta.apache.org by se...@apache.org on 2005/11/27 03:07:42 UTC
svn commit: r349168 -
/jakarta/jmeter/branches/rel-2-1/src/jorphan/org/apache/jorphan/reflect/ClassFinder.java
Author: sebb
Date: Sat Nov 26 18:07:38 2005
New Revision: 349168
URL: http://svn.apache.org/viewcvs?rev=349168&view=rev
Log:
Tidy up the code and make it a bit more efficient
Modified:
jakarta/jmeter/branches/rel-2-1/src/jorphan/org/apache/jorphan/reflect/ClassFinder.java
Modified: jakarta/jmeter/branches/rel-2-1/src/jorphan/org/apache/jorphan/reflect/ClassFinder.java
URL: http://svn.apache.org/viewcvs/jakarta/jmeter/branches/rel-2-1/src/jorphan/org/apache/jorphan/reflect/ClassFinder.java?rev=349168&r1=349167&r2=349168&view=diff
==============================================================================
--- jakarta/jmeter/branches/rel-2-1/src/jorphan/org/apache/jorphan/reflect/ClassFinder.java (original)
+++ jakarta/jmeter/branches/rel-2-1/src/jorphan/org/apache/jorphan/reflect/ClassFinder.java Sat Nov 26 18:07:38 2005
@@ -1,6 +1,5 @@
-// $Header$
/*
- * Copyright 2001-2004 The Apache Software Foundation.
+ * Copyright 2001-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,129 +31,183 @@
import java.util.TreeSet;
import java.util.zip.ZipFile;
+import org.apache.jmeter.util.StringUtilities;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
/**
- * This class finds classes that implement one or more specified interfaces.
+ * This class finds classes that extend one of a set of parent classes
*
* @author Burt Beckwith
* @author Michael Stover (mstover1 at apache.org)
* @version $Revision$
*/
public final class ClassFinder {
- transient private static Logger log = LoggingManager.getLoggerForClass();
+ private static final Logger log = LoggingManager.getLoggerForClass();
+ 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 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;
+ }
+ }
- // static only
/**
- * Convenience method for <code>findClassesThatExtend(Class[],
- * boolean)</code>
+ * Convenience method for
+ * <code>findClassesThatExtend(Class[], boolean)</code>
* with the option to include inner classes in the search set to false.
*
- * @return ArrayList containing discovered classes.
+ * @return List containing discovered classes.
*/
- public static List findClassesThatExtend(String[] paths, Class[] superClasses) throws IOException,
- ClassNotFoundException {
+ public static List findClassesThatExtend(String[] paths, Class[] superClasses)
+ throws IOException, ClassNotFoundException {
return findClassesThatExtend(paths, superClasses, false);
}
- /**
- * Find classes in the provided path(s)/jar(s) that extend the class(es).
- *
- * @return ArrayList containing discovered classes
- */
- private static String[] addJarsInPath(String[] paths) {
- Set fullList = new HashSet();
- for (int i = 0; i < paths.length; i++) {
- fullList.add(paths[i]);
- if (!paths[i].endsWith(".jar")) {
- File dir = new File(paths[i]);
- if (dir.exists() && dir.isDirectory()) {
- String[] jars = dir.list(new FilenameFilter() {
- public boolean accept(File f, String name) {
- if (name.endsWith(".jar")) {
- return true;
- }
- return false;
- }
- });
- for (int x = 0; x < jars.length; x++) {
- fullList.add(jars[x]);
- }
- }
- }
- }
- return (String[]) fullList.toArray(new String[0]);
- }
-
- public static List findClassesThatExtend(String[] strPathsOrJars, Class[] superClasses, boolean innerClasses)
+ // For each directory in the search path, add all the jars found there
+ private static String[] addJarsInPath(String[] paths) {
+ Set fullList = new HashSet();
+ for (int i = 0; i < paths.length; i++) {
+ final String path = paths[i];
+ 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);
+ }
+ });
+ for (int x = 0; x < jars.length; x++) {
+ fullList.add(jars[x]);
+ }
+ }
+ }
+ }
+ return (String[]) fullList.toArray(new String[0]);
+ }
+
+ /**
+ * Find classes in the provided path(s)/jar(s) that extend the class(es).
+ * @param strPathsOrJars - pathnames or jarfiles to search for classes
+ * @param superClasses - required parent class(es)
+ * @param innerClasses - should we include inner classes?
+ *
+ * @return List containing discovered classes
+ */
+ public static List findClassesThatExtend(String[] strPathsOrJars,
+ final Class[] superClasses, final boolean innerClasses)
throws IOException, ClassNotFoundException {
- List listPaths = null;
- Set listClasses = null;
- List listSuperClasses = null;
+
+ // Find all jars in the search path
strPathsOrJars = addJarsInPath(strPathsOrJars);
- if (log.isDebugEnabled()) {
- for (int k = 0; k < strPathsOrJars.length; k++) {
+ for (int k = 0; k < strPathsOrJars.length; k++) {
+ strPathsOrJars[k] = fixPathEntry(strPathsOrJars[k]);
+ if (log.isDebugEnabled()) {
log.debug("strPathsOrJars : " + strPathsOrJars[k]);
}
}
- listPaths = getClasspathMatches(strPathsOrJars);
+
+ // Now eliminate any classpath entries that do not "match" the search
+ List listPaths = getClasspathMatches(strPathsOrJars);
if (log.isDebugEnabled()) {
Iterator tIter = listPaths.iterator();
- for (; tIter.hasNext();) {
+ while (tIter.hasNext()) {
log.debug("listPaths : " + tIter.next());
}
}
- listClasses = new TreeSet();
- listSuperClasses = new ArrayList();
- for (int i = 0; i < superClasses.length; i++) {
- listSuperClasses.add(superClasses[i].getName());
- }
+
+ Set listClasses = new FilterTreeSet(superClasses, innerClasses);
// first get all the classes
findClassesInPaths(listPaths, listClasses);
if (log.isDebugEnabled()) {
+ log.debug("listClasses.size()="+listClasses.size());
Iterator tIter = listClasses.iterator();
- for (; tIter.hasNext();) {
+ while (tIter.hasNext()) {
log.debug("listClasses : " + tIter.next());
}
}
- Set subClassList = findAllSubclasses(listSuperClasses, listClasses, innerClasses);
- return new ArrayList(subClassList);
- }
-
+
+// // 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(listClasses);//subClassList);
+ }
+
+ /*
+ * Returns the classpath entries that match the search list of jars and paths
+ */
private static List getClasspathMatches(String[] strPathsOrJars) {
- ArrayList listPaths = null;
- StringTokenizer stPaths = null;
- String strPath = null;
- int i;
- listPaths = new ArrayList();
- log.debug("Classpath = " + System.getProperty("java.class.path"));
- stPaths = new StringTokenizer(System.getProperty("java.class.path"), System.getProperty("path.separator"));
- if (strPathsOrJars != null) {
- strPathsOrJars = fixDotDirs(strPathsOrJars);
- strPathsOrJars = fixSlashes(strPathsOrJars);
- strPathsOrJars = fixEndingSlashes(strPathsOrJars);
- }
+ log.debug("Classpath = " + System.getProperty("java.class.path")); // $NON-NLS-1$
+ StringTokenizer stPaths =
+ new StringTokenizer(System.getProperty("java.class.path"), // $NON-NLS-1$
+ System.getProperty("path.separator")); // $NON-NLS-1$
if (log.isDebugEnabled()) {
- for (i = 0; i < strPathsOrJars.length; i++) {
+ for (int i = 0; i < strPathsOrJars.length; i++) {
log.debug("strPathsOrJars[" + i + "] : " + strPathsOrJars[i]);
}
}
// find all jar files or paths that end with strPathOrJar
+ ArrayList listPaths = new ArrayList();
+ String strPath = null;
while (stPaths.hasMoreTokens()) {
- strPath = fixDotDir(stPaths.nextToken());
- strPath = fixSlashes(strPath);
- strPath = fixEndingSlashes(strPath);
+ strPath = fixPathEntry(stPaths.nextToken());
if (strPathsOrJars == null) {
log.debug("Adding: " + strPath);
listPaths.add(strPath);
} else {
boolean found = false;
- for (i = 0; i < strPathsOrJars.length; i++) {
+ for (int i = 0; i < strPathsOrJars.length; i++) {
if (strPath.endsWith(strPathsOrJars[i])) {
found = true;
log.debug("Adding " + strPath + " found at " + i);
@@ -170,97 +223,29 @@
return listPaths;
}
- /**
- * Get all interfaces that the class implements, including parent
- * interfaces. This keeps us from having to instantiate and check
- * instanceof, which wouldn't work anyway since instanceof requires a
- * hard-coded class or interface name.
- *
- * @param theClass
- * the class to get interfaces for
- * @param hInterfaces
- * a Map to store the discovered interfaces in
- *
- * NOTUSED private static void getAllInterfaces(Class theClass, Map
- * hInterfaces) { Class[] interfaces = theClass.getInterfaces(); for (int i =
- * 0; i < interfaces.length; i++) { hInterfaces.put(interfaces[i].getName(),
- * interfaces[i]); getAllInterfaces(interfaces[i], hInterfaces); } }
- */
- private static String[] fixDotDirs(String[] paths) {
- for (int i = 0; i < paths.length; i++) {
- paths[i] = fixDotDir(paths[i]);
- }
- return paths;
- }
-
- private static String fixDotDir(String path) {
- if (path == null)
- return null;
- if (path.equals(".")) {
- return System.getProperty("user.dir");
- } else {
- return path.trim();
- }
- }
-
- private static String[] fixEndingSlashes(String[] strings) {
- String[] strNew = new String[strings.length];
- for (int i = 0; i < strings.length; i++) {
- strNew[i] = fixEndingSlashes(strings[i]);
- }
- return strNew;
- }
-
- private static String fixEndingSlashes(String string) {
- if (string.endsWith("/") || string.endsWith("\\")) {
- string = string.substring(0, string.length() - 1);
- string = fixEndingSlashes(string);
- }
- return string;
- }
-
- private static String[] fixSlashes(String[] strings) {
- String[] strNew = new String[strings.length];
- for (int i = 0; i < strings.length; i++) {
- strNew[i] = fixSlashes(strings[i]) /* .toLowerCase() */;
- }
- return strNew;
- }
-
- private static String fixSlashes(String str) {
- // replace \ with /
- str = str.replace('\\', '/');
- // compress multiples into singles;
- // do in 2 steps with dummy string
- // to avoid infinte loop
- str = replaceString(str, "//", "_____");
- str = replaceString(str, "_____", "/");
- return str;
- }
-
- private static String replaceString(String s, String strToFind, String strToReplace) {
- int index;
- int currentPos;
- StringBuffer buffer = null;
- if (s.indexOf(strToFind) == -1) {
- return s;
- }
- currentPos = 0;
- buffer = new StringBuffer();
- while (true) {
- index = s.indexOf(strToFind, currentPos);
- if (index == -1) {
- break;
- }
- buffer.append(s.substring(currentPos, index));
- buffer.append(strToReplace);
- currentPos = index + strToFind.length();
- }
- buffer.append(s.substring(currentPos));
- return buffer.toString();
- }
-
- /**
+ /**
+ * Fix a path:
+ * - replace "." by current directory
+ * - trim any trailing spaces
+ * - replace \ by /
+ * - replace // by /
+ * - remove all trailing /
+ */
+ private static String fixPathEntry(String path){
+ if (path == null ) return null;
+ if (path.equals(".")) { // $NON-NLS-1$
+ return System.getProperty("user.dir"); // $NON-NLS-1$
+ }
+ path = path.trim().replace('\\', '/'); // $NON-NLS-1$ // $NON-NLS-2$
+ path = StringUtilities.substitute(path, "//", "/"); // $NON-NLS-1$// $NON-NLS-2$
+
+ 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
@@ -278,27 +263,11 @@
* theInterface) { return true; } } return false; }
*/
- /**
- * Convenience method for <code>findAllSubclasses(List, List,
- * boolean)</code>
- * with the option to include inner classes in the search set to false.
- *
- * @param listSuperClasses
- * the base classes to find subclasses for
- * @param listAllClasses
- * the collection of classes to search in
- * @return ArrayList of the subclasses
- *
- * NOTUSED private static ArrayList findAllSubclasses( List
- * listSuperClasses, List listAllClasses) { return
- * findAllSubclasses(listSuperClasses, listAllClasses, false); }
- */
-
- /**
+ /*
* Finds all classes that extend the classes in the listSuperClasses
* ArrayList, searching in the listAllClasses ArrayList.
*
- * @param listSuperClasses
+ * @param superClasses
* the base classes to find subclasses for
* @param listAllClasses
* the collection of classes to search in
@@ -306,48 +275,15 @@
* indicate whether to include inner classes in the search
* @return ArrayList of the subclasses
*/
- private static Set findAllSubclasses(List listSuperClasses, Set listAllClasses, boolean innerClasses) {
- Iterator iterClasses = null;
- Set listSubClasses = null;
- String strClassName = null;
- Class tempClass = null;
- listSubClasses = new TreeSet();
- iterClasses = listSuperClasses.iterator();
- while (iterClasses.hasNext()) {
- 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) {
- // might throw an exception, assume this is ignorable
- try {
- tempClass = Class.forName(strClassName, false, Thread.currentThread().getContextClassLoader());
- findAllSubclassesOneClass(tempClass, listAllClasses, listSubClasses, innerClasses);
- // call by reference - recursive
- } catch (Throwable ignored) {
- }
- }
- }
- return listSubClasses;
- }
+// 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;
+// }
- /**
- * Convenience method for <code>findAllSubclassesOneClass(Class, List, List,
- * boolean)</code>
- * with option to include inner classes in the search set to false.
- *
- * @param theClass
- * the parent class
- * @param listAllClasses
- * the collection of classes to search in
- * @param listSubClasses
- * the collection of discovered subclasses
- *
- * NOTUSED private static void findAllSubclassesOneClass( Class theClass,
- * List listAllClasses, List listSubClasses) { findAllSubclassesOneClass(
- * theClass, listAllClasses, listSubClasses, false); }
- */
-
- /**
+ /*
* Finds all classes that extend the class, searching in the listAllClasses
* ArrayList.
*
@@ -361,37 +297,59 @@
* indicates whether inners classes should be included in the
* search
*/
- private static void findAllSubclassesOneClass(Class theClass, Set listAllClasses, Set listSubClasses,
- boolean innerClasses) {
- Iterator iterClasses = null;
- String strClassName = null;
- Class c = null;
- boolean bIsSubclass = false;
- iterClasses = listAllClasses.iterator();
- while (iterClasses.hasNext()) {
- 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) {
- // might throw an exception, assume this is ignorable
- try {
- c = Class.forName(strClassName, false, Thread.currentThread().getContextClassLoader());
-
- if (!c.isInterface() && !Modifier.isAbstract(c.getModifiers())) {
- bIsSubclass = theClass.isAssignableFrom(c);
- } else {
- bIsSubclass = false;
- }
- if (bIsSubclass) {
- listSubClasses.add(strClassName);
- }
- } catch (Throwable ignored) {
- }
- }
- }
- }
-
- /**
+// 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 innerClasses should we allow inner classes?
+ * @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 (int i=0; i< parentClasses.length; i++) {
+ if(parentClasses[i].isAssignableFrom(c)){
+ return true;
+ }
+ }
+ }
+ } catch (Throwable ignored) {
+ log.debug(ignored.getLocalizedMessage());
+ }
+ return false;
+ }
+
+
+ /*
* Converts a class file from the text stored in a Jar file to a version
* that can be used in Class.forName().
*
@@ -400,27 +358,23 @@
* @return String the Java-style dotted version of the name
*/
private static String fixClassName(String strClassName) {
- strClassName = strClassName.replace('\\', '.');
- strClassName = strClassName.replace('/', '.');
- strClassName = strClassName.substring(0, strClassName.length() - 6);
- // remove ".class"
+ 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 listClasses) throws IOException {
- File file = null;
- ZipFile zipFile = null;
- Enumeration entries = null;
- String strEntry = null;
- file = new File(strPath);
+ File file = new File(strPath);
if (file.isDirectory()) {
findClassesInPathsDir(strPath, file, listClasses);
} else if (file.exists()) {
- zipFile = new ZipFile(file);
- entries = zipFile.entries();
+ ZipFile zipFile = new ZipFile(file);
+ Enumeration entries = zipFile.entries();
while (entries.hasMoreElements()) {
- strEntry = entries.nextElement().toString();
- if (strEntry.endsWith(".class")) {
+ String strEntry = entries.nextElement().toString();
+ if (strEntry.endsWith(DOT_CLASS)) {
listClasses.add(fixClassName(strEntry));
}
}
@@ -435,15 +389,17 @@
}
private static void findClassesInPathsDir(String strPathElement, File dir, Set listClasses) throws IOException {
- File file = null;
String[] list = dir.list();
for (int i = 0; i < list.length; i++) {
- file = new File(dir, list[i]);
+ File file = new File(dir, list[i]);
if (file.isDirectory()) {
+ // Recursive call
findClassesInPathsDir(strPathElement, file, listClasses);
- } else if (file.exists() && (file.length() != 0) && list[i].endsWith(".class")) {
- listClasses.add(file.getPath().substring(strPathElement.length() + 1, file.getPath().lastIndexOf("."))
- .replace(File.separator.charAt(0), '.'));
+ } 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$
}
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: jmeter-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: jmeter-dev-help@jakarta.apache.org