|
What this is
This file is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Other links
The source code
// $Header: /home/cvs/jakarta-jmeter/src/jorphan/org/apache/jorphan/reflect/ClassFinder.java,v 1.9 2004/02/11 23:57:23 sebb Exp $
/*
* Copyright 2001-2004 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.
* 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.zip.ZipFile;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
/**
* This class finds classes that implement one or more specified interfaces.
*
* @author Burt Beckwith
* @author Michael Stover (mstover1 at apache.org)
* @version $Revision: 1.9 $
*/
public final class ClassFinder
{
transient private static Logger log = LoggingManager.getLoggerForClass();
private ClassFinder()
{
}
// static only
/**
* Convenience method for findClassesThatExtend(Class[],
* boolean) with the option to include inner classes in the search
* set to false.
*
* @return ArrayList containing discovered classes.
*/
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)
throws IOException, ClassNotFoundException
{
List listPaths = null;
ArrayList listClasses = null;
List listSuperClasses = null;
strPathsOrJars = addJarsInPath(strPathsOrJars);
if (log.isDebugEnabled())
{
for (int k = 0; k < strPathsOrJars.length; k++)
{
log.debug("strPathsOrJars : " + strPathsOrJars[k]);
}
}
listPaths = getClasspathMatches(strPathsOrJars);
if (log.isDebugEnabled())
{
Iterator tIter = listPaths.iterator();
for (; tIter.hasNext();)
{
log.debug("listPaths : " + tIter.next());
}
}
listClasses = new ArrayList();
listSuperClasses = new ArrayList();
for (int i = 0; i < superClasses.length; i++)
{
listSuperClasses.add(superClasses[i].getName());
}
// first get all the classes
findClassesInPaths(listPaths, listClasses);
if (log.isDebugEnabled())
{
Iterator tIter = listClasses.iterator();
for (; tIter.hasNext();)
{
log.debug("listClasses : " + tIter.next());
}
}
List subClassList =
findAllSubclasses(listSuperClasses, listClasses, innerClasses);
return subClassList;
}
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);
}
if (log.isDebugEnabled())
{
for (i = 0; i < strPathsOrJars.length; i++)
{
log.debug("strPathsOrJars[" + i + "] : " + strPathsOrJars[i]);
}
}
// find all jar files or paths that end with strPathOrJar
while (stPaths.hasMoreTokens())
{
strPath = fixDotDir((String) stPaths.nextToken());
strPath = fixSlashes(strPath);
strPath = fixEndingSlashes(strPath);
if (strPathsOrJars == null)
{
log.debug("Adding: " + strPath);
listPaths.add(strPath);
}
else
{
boolean found=false;
for (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;
}
/**
* 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();
}
/**
* 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;
}
*/
/**
* Convenience method for findAllSubclasses(List, List,
* boolean) 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 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 ArrayList findAllSubclasses(
List listSuperClasses,
List listAllClasses,
boolean innerClasses)
{
Iterator iterClasses = null;
ArrayList listSubClasses = null;
String strClassName = null;
Class tempClass = null;
listSubClasses = new ArrayList();
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;
}
/**
* Convenience method for findAllSubclassesOneClass(Class, List, List,
* boolean) 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.
*
* @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,
List listAllClasses,
List 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)
{
}
}
}
}
/**
* 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('\\', '.');
strClassName = strClassName.replace('/', '.');
strClassName = strClassName.substring(0, strClassName.length() - 6);
// remove ".class"
return strClassName;
}
private static void findClassesInOnePath(String strPath, List listClasses)
throws IOException
{
File file = null;
ZipFile zipFile = null;
Enumeration entries = null;
String strEntry = null;
file = new File(strPath);
if (file.isDirectory())
{
findClassesInPathsDir(strPath, file, listClasses);
}
else if (file.exists())
{
zipFile = new ZipFile(file);
entries = zipFile.entries();
while (entries.hasMoreElements())
{
strEntry = entries.nextElement().toString();
if (strEntry.endsWith(".class"))
{
listClasses.add(fixClassName(strEntry));
}
}
}
}
private static void findClassesInPaths(List listPaths, List listClasses)
throws IOException
{
Iterator iterPaths = listPaths.iterator();
while (iterPaths.hasNext())
{
findClassesInOnePath((String) iterPaths.next(), listClasses);
}
}
private static void findClassesInPathsDir(
String strPathElement,
File dir,
List listClasses)
throws IOException
{
File file = null;
String[] list = dir.list();
for (int i = 0; i < list.length; i++)
{
file = new File(dir, list[i]);
if (file.isDirectory())
{
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), '.'));
}
}
}
}
|