/*
* Copyright (C) 2013 Dr. John Lindsay <jlindsay@uoguelph.ca>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package whitebox.utilities;
//import java.util.zip.ZipInputStream;
//import java.util.zip.ZipEntry;
//import java.net.MalformedURLException;
//import java.net.URLDecoder;
//import java.util.TreeSet;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* This class can be used to identify the classes within a jar file or
* directory.
*
* @author Dave Dopson, modified by johnlindsay
*/
public class ClassEnumerator {
private static void log(String msg) {
System.out.println("ClassDiscovery: " + msg);
}
private static Class<?> loadClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unexpected ClassNotFoundException loading class '" + className + "'");
}
}
private static void processDirectory(File directory, String pkgname, ArrayList<Class<?>> classes) {
log("Reading Directory '" + directory + "'");
// Get the list of the files contained in the package
String[] files = directory.list();
for (int i = 0; i < files.length; i++) {
String fileName = files[i];
String className = null;
// we are only interested in .class files
if (fileName.endsWith(".class")) {
// removes the .class extension
className = pkgname + '.' + fileName.substring(0, fileName.length() - 6);
}
log("FileName '" + fileName + "' => class '" + className + "'");
if (className != null) {
classes.add(loadClass(className));
}
File subdir = new File(directory, fileName);
if (subdir.isDirectory()) {
processDirectory(subdir, pkgname + '.' + fileName, classes);
}
}
}
private static void processJarfile(URL resource, String pkgname, ArrayList<Class<?>> classes) {
String relPath = pkgname.replace('.', '/');
String resPath = resource.getPath();
String jarPath = resPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", "");
log("Reading JAR file: '" + jarPath + "'");
JarFile jarFile;
try {
jarFile = new JarFile(jarPath);
} catch (IOException e) {
return;
//throw new RuntimeException("Unexpected IOException reading JAR File '" + jarPath + "'", e);
}
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
String className = null;
if (entryName.endsWith(".class") && entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) {
className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
}
log("JarEntry '" + entryName + "' => class '" + className + "'");
if (className != null) {
classes.add(loadClass(className));
}
}
}
private static void processDirectoryForClassNames(File directory, String pkgname, ArrayList<String> classes) {
//log("Reading Directory '" + directory + "'");
// Get the list of the files contained in the package
String[] files = directory.list();
for (int i = 0; i < files.length; i++) {
String fileName = files[i];
String className = null;
// we are only interested in .class files
if (fileName.endsWith(".class")) {
// removes the .class extension
className = pkgname + '.' + fileName.substring(0, fileName.length() - 6);
}
//log("FileName '" + fileName + "' => class '" + className + "'");
if (className != null && !className.contains("$")) {
classes.add(className);
} //else if (className == null && !fileName.contains("$") && !fileName.endsWith(".properties")) {
// fileName = fileName.replace('/', '.').replace('\\', '.').replace(".class", "");
// if (fileName.endsWith(".")) {
// fileName = fileName.substring(0, fileName.length() - 1);
// }
// classes.add(fileName);
// }
File subdir = new File(directory, fileName);
if (subdir.isDirectory()) {
processDirectoryForClassNames(subdir, pkgname + '.' + fileName, classes);
}
}
}
private static void processJarfileForClassNames(URL resource, String pkgname, ArrayList<String> classes) {
String relPath = pkgname.replace('.', '/');
String resPath = resource.getPath();
String jarPath = resPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", "");
//log("Reading JAR file: '" + jarPath + "'");
JarFile jarFile;
try {
jarFile = new JarFile(jarPath);
} catch (IOException e) {
return;
//throw new RuntimeException("Unexpected IOException reading JAR File '" + jarPath + "'", e);
}
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
String className = null;
if (entryName.startsWith(relPath)) { // && entryName.length() > (relPath.length() + 1)) {
if (entryName.endsWith(".class")) {
className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
}
if (className != null && !className.contains("$")) {
classes.add(className);
} else if (className == null && !entryName.contains("$") && !entryName.endsWith(".properties")) {
entryName = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
if (entryName.endsWith(".")) {
entryName = entryName.substring(0, entryName.length() - 1);
}
classes.add(entryName);
}
}
}
}
public static ArrayList<String> getClassNamesForPackage(Package pkg) {
ArrayList<String> classes = new ArrayList<>();
String pkgname = pkg.getName();
String relPath = pkgname.replace('.', '/');
// Get a File object for the package
URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
if (resource == null) {
return null;
//throw new RuntimeException("Unexpected problem: No resource for " + relPath);
}
//log("Package: '" + pkgname + "' becomes Resource: '" + resource.toString() + "'");
resource.getPath();
if (resource.toString().startsWith("jar:")) {
processJarfileForClassNames(resource, pkgname, classes);
} else {
processDirectoryForClassNames(new File(resource.getPath()), pkgname, classes);
}
return classes;
}
public static ArrayList<String> getClassNamesForPackage(String pkgname) {
ArrayList<String> classes = new ArrayList<>();
String relPath = pkgname.replace('.', '/');
// Get a File object for the package
URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
if (resource == null) {
return null;
//throw new RuntimeException("Unexpected problem: No resource for " + relPath);
}
//log("Package: '" + pkgname + "' becomes Resource: '" + resource.toString() + "'");
resource.getPath();
if (resource.toString().startsWith("jar:")) {
processJarfileForClassNames(resource, pkgname, classes);
} else {
processDirectoryForClassNames(new File(resource.getPath()), pkgname, classes);
}
return classes;
// try {
// ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// assert classLoader != null;
// String path = pkgname.replace('.', '/');
// Enumeration resources = classLoader.getResources(path);
// ArrayList<String> dirs = new ArrayList();
// while (resources.hasMoreElements()) {
// URL resource = (URL) resources.nextElement();
// dirs.add(URLDecoder.decode(resource.getFile(), "UTF-8"));
// }
// TreeSet classes = new TreeSet();
// for (String directory : dirs) {
// classes.addAll(findClasses(directory, pkgname));
// }
// ArrayList classList = new ArrayList();
// for (Object clazz : classes) {
// classList.add(Class.forName((String) clazz));
// }
// return classList;
// } catch (Exception e) {
// return null;
// // do nothing
// }
////// }
// private static TreeSet findClasses(String path, String packageName) throws MalformedURLException, IOException {
// TreeSet classes = new TreeSet();
// if (path.startsWith("file:") && path.contains("!")) {
// String[] split = path.split("!");
// URL jar = new URL(split[0]);
// ZipInputStream zip = new ZipInputStream(jar.openStream());
// ZipEntry entry;
// while ((entry = zip.getNextEntry()) != null) {
// if (entry.getName().endsWith(".class")) {
// String className = entry.getName().replaceAll("[$].*", "").replaceAll("[.]class", "").replace('/', '.');
// if (className.startsWith(packageName)) {
// classes.add(className);
// }
// }
// }
// }
// File dir = new File(path);
// if (!dir.exists()) {
// return classes;
// }
// File[] files = dir.listFiles();
// for (File file : files) {
// if (file.isDirectory()) {
// assert !file.getName().contains(".");
// classes.addAll(findClasses(file.getAbsolutePath(), packageName + "." + file.getName()));
// } else if (file.getName().endsWith(".class")) {
// String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
// classes.add(className);
// }
// }
// return classes;
}
public static ArrayList<Class<?>> getClassesForPackage(Package pkg) {
ArrayList<Class<?>> classes = new ArrayList<>();
String pkgname = pkg.getName();
String relPath = pkgname.replace('.', '/');
// Get a File object for the package
URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
if (resource == null) {
throw new RuntimeException("Unexpected problem: No resource for " + relPath);
}
log("Package: '" + pkgname + "' becomes Resource: '" + resource.toString() + "'");
resource.getPath();
if (resource.toString().startsWith("jar:")) {
processJarfile(resource, pkgname, classes);
} else {
processDirectory(new File(resource.getPath()), pkgname, classes);
}
return classes;
}
}