/*
* Copyright 2005-2010 Ignis Software Tools Ltd. All rights reserved.
*/
package jsystem.utils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import jsystem.runner.loader.ExtendsTestCaseClassLoader;
/**
* Utility class for classpath related operations.
*
* @author guy.arieli & Golan Derazon
*/
public class ClassSearchUtil {
/**
* Used to scan a class path (jars and folders) and search for a specific implementation for a class.
* @param ofType the class to search implementations for.
* @param classPath the class path to search in.
* @param filterAbstract if set to true will filter abstract implementations.
* @param ignoreList a list of string that if found in the class path element name will ignore it.
* @return an array with all the class name implementations found.
* @throws Exception when the search process fail.
*/
public static ArrayList<String> searchClasses(Class<?> ofType, String classPath, boolean filterAbstract, String[] ignoreList, String[] includeList) throws Exception{
GeneralCollector gc = new GeneralCollector(ofType, classPath, filterAbstract, ignoreList, includeList);
ArrayList<String> tv = gc.collectTestsVector();
return tv;
}
/**
* Fetches a property from a properties file in the classpath.
* If properties file not founf in class path the method throws an <code>IOException</code>.
* If property doesn't exist in properties file, <code>null</code> is returned.
* @param propertyFileResourcePath path of the properties file in the class path
* @param name of property to fetch
*/
public static String getPropertyFromClassPath(String propertyFileResourcePath,String propertyName ) throws Exception {
Properties p = new Properties();
InputStream inStream = null;
try {
inStream = ClassSearchUtil.class.getClassLoader().getResourceAsStream(propertyFileResourcePath);
if (inStream == null){
throw new IOException("Property file not found in classpath. " + propertyFileResourcePath );
}
p.load(inStream);
} finally{
if (inStream != null){
inStream.close();
}
}
return p.getProperty(propertyName);
}
}
class GeneralCollector {
private static Logger log = Logger.getLogger(GeneralCollector.class.getName());
private Class<?> ofType = null;
private String classPath;
private boolean filterAbstract;
private ClassLoader loader;
private String[] ignoreList = null;
private String[] includeList = null;
public GeneralCollector(Class<?> ofType, String classPath, boolean filterAbstract, String[] ignoreList, String[] includeList) throws Exception{
this.classPath = classPath;
this.filterAbstract = filterAbstract;
loader = new ExtendsTestCaseClassLoader(classPath, getClass().getClassLoader().getParent());
this.ignoreList = ignoreList;
this.includeList = includeList;
this.ofType = loader.loadClass(ofType.getName());
}
public Enumeration<String> collectTests() {
Hashtable<String, String> result = collectFilesInPath(classPath);
return result.elements();
}
public ArrayList<String> collectTestsVector() {
Enumeration<String> en = collectTests();
ArrayList<String> v = new ArrayList<String>(10);
while (en.hasMoreElements()) {
v.add(en.nextElement());
}
return v;
}
public boolean isTestClass(String classFileName) {
Class<?> c;
if(classFileName == null || classFileName.length() < 7){
return false;
}
String className = classFileName.substring(1, classFileName.length() - 6).replace('\\', '.').replace('/', '.');
try {
c = loader.loadClass(className);
} catch (Throwable e) {
return false;
}
if(filterAbstract && Modifier.isAbstract(c.getModifiers())){
return false;
}
if(c == null){
return false;
}
return ofType.isAssignableFrom(c);
}
protected String classNameFromFile(String classFileName) {
return classFileName.substring(1, classFileName.length() - 6).replace('/', '.').replace('\\', '.');
}
public Hashtable<String,String> collectFilesInPath(String classPath) {
Hashtable<String, String> result= collectFilesInRoots(splitClassPath(classPath));
return result;
}
private Hashtable<String, String> collectFilesInRoots(Vector<String> roots) {
Hashtable<String, String> result= new Hashtable<String, String>(100);
Enumeration<String> e= roots.elements();
whilecont:
while (e.hasMoreElements()) {
String classElement = (String)e.nextElement();
if(includeList != null){
boolean found = false;
for(String toInclude: includeList){
if(classElement.toLowerCase().endsWith(".jar")){
if(classElement.toLowerCase().contains(toInclude.toLowerCase())){
found = true;
break;
}
} else{
found = true;
break;
}
}
if(!found){
continue;
}
} else if(ignoreList != null){
for(String toIgnore: ignoreList){
if(classElement.toLowerCase().contains(toIgnore.toLowerCase())){
continue whilecont;
}
}
}
try {
gatherFiles(new File(classElement), "", result);
} catch (Exception e1) {
log.log(Level.INFO, "Fail to process file", e1);
}
}
return result;
}
private void gatherFiles(File classRoot, String classFileName, Hashtable<String, String> result) throws Exception {
File thisRoot= new File(classRoot, classFileName);
if(Thread.interrupted()){
throw new InterruptedException();
}
if (thisRoot.isFile()) {
if(thisRoot.getName().toLowerCase().endsWith(".jar")){
processJar(thisRoot, result);
}
if (isTestClass(classFileName)) {
String className= classNameFromFile(classFileName);
result.put(className, className);
}
return;
}
String[] contents= thisRoot.list();
if (contents != null) {
for (int i= 0; i < contents.length; i++)
gatherFiles(classRoot, classFileName+File.separatorChar+contents[i], result);
}
}
private void processJar(File jarFile, Hashtable<String, String> result) throws Exception{
ZipFile zipFile = new ZipFile(jarFile);
Enumeration<? extends ZipEntry> enties = zipFile.entries();
while (enties.hasMoreElements()){
if(Thread.interrupted()){
throw new InterruptedException();
}
ZipEntry entry = (ZipEntry)enties.nextElement();
if(entry.isDirectory()){
continue;
}
if (isTestClass("/" + entry.getName())) {
String className= classNameFromFile("/" + entry.getName());
result.put(className, className);
}
}
}
private Vector<String> splitClassPath(String classPath) {
Vector<String> result= new Vector<String>();
String separator= System.getProperty("path.separator");
StringTokenizer tokenizer= new StringTokenizer(classPath, separator);
while (tokenizer.hasMoreTokens())
result.addElement(tokenizer.nextToken());
return result;
}
}