ClassFinder.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
org.apache.jorphan.reflect |
![]() |
![]() |
JMeter |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.jorphan.reflect;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.zip.ZipFile;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.jorphan.util.JOrphanUtils;
import org.apache.log.Logger;
/**
* This class finds classes that extend one of a set of parent classes
*
*/
public final class ClassFinder {
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?
// hack to reduce the need to load every class in non-GUI mode, which only needs functions
// TODO perhaps use BCEL to scan class files instead?
private final String contains; // class name should contain this string
private final String notContains; // class name should not contain this string
private final transient ClassLoader contextClassLoader
= Thread.currentThread().getContextClassLoader(); // Potentially expensive; do it once
FilterTreeSet(Class []parents, boolean inner, String contains, String notContains){
super();
this.parents=parents;
this.inner=inner;
this.contains=contains;
this.notContains=notContains;
}
/**
* 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 (contains!=null && s.indexOf(contains) == -1){
return false; // It does not contain a required string
}
if (notContains!=null && s.indexOf(notContains) != -1){
return false; // It contains a banned string
}
if ((s.indexOf("$") == -1) || inner) { // $NON-NLS-1$
if (isChildOf(parents,s, contextClassLoader)) {
return super.add(s);
}
}
return false;
}
}
/**
* Convenience method for
* <code>findClassesThatExtend(Class[], boolean)</code>
* with the option to include inner classes in the search set to false.
*
* @return List containing discovered classes.
*/
public static List 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 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 {
return findClassesThatExtend(strPathsOrJars,superClasses,innerClasses,null,null);
}
/**
* 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?
* @param contains - classname should contain this string
* @param notContains - classname should not contain this string
*
* @return List containing discovered classes
*/
public static List findClassesThatExtend(String[] strPathsOrJars,
final Class[] superClasses, final boolean innerClasses,
String contains, String notContains)
throws IOException {
if (log.isDebugEnabled()) {
for (int i = 0; i < superClasses.length ; i++){
log.debug("superclass: "+superClasses[i].getName());
}
}
// Find all jars in the search path
strPathsOrJars = addJarsInPath(strPathsOrJars);
for (int k = 0; k < strPathsOrJars.length; k++) {
strPathsOrJars[k] = fixPathEntry(strPathsOrJars[k]);
if (log.isDebugEnabled()) {
log.debug("strPathsOrJars : " + strPathsOrJars[k]);
}
}
// Now eliminate any classpath entries that do not "match" the search
List listPaths = getClasspathMatches(strPathsOrJars);
if (log.isDebugEnabled()) {
Iterator tIter = listPaths.iterator();
while (tIter.hasNext()) {
log.debug("listPaths : " + tIter.next());
}
}
Set listClasses = new FilterTreeSet(superClasses, innerClasses, contains, notContains);
// first get all the classes
findClassesInPaths(listPaths, listClasses);
if (log.isDebugEnabled()) {
log.debug("listClasses.size()="+listClasses.size());
Iterator tIter = listClasses.iterator();
while (tIter.hasNext()) {
log.debug("listClasses : " + tIter.next());
}
}
// // 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) {
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 (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 = fixPathEntry(stPaths.nextToken());
if (strPathsOrJars == null) {
log.debug("Adding: " + strPath);
listPaths.add(strPath);
} else {
boolean found = false;
for (int i = 0; i < strPathsOrJars.length; i++) {
if (strPath.endsWith(strPathsOrJars[i])) {
found = true;
log.debug("Adding " + strPath + " found at " + i);
listPaths.add(strPath);
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 /
*/
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 = JOrphanUtils.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
* 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 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().
*
* @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 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));
}
}
}
}
private static void findClassesInPaths(List listPaths, Set listClasses) throws IOException {
Iterator iterPaths = listPaths.iterator();
while (iterPaths.hasNext()) {
findClassesInOnePath((String) iterPaths.next(), listClasses);
}
}
private static void findClassesInPathsDir(String strPathElement, File dir, Set 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$
}
}
}
}
The table below shows all metrics for ClassFinder.java.




