package io.vertx.codegen; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.function.Function; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Logger; /** * Based on: * * https://github.com/ddopson/java-class-enumerator * * Which has the following license: * * ***** * * This code was originally a snippet on StackOverflow, thus it has the following license: * * http://creativecommons.org/licenses/by-sa/2.5/ * * I deduced this from http://meta.stackexchange.com/questions/25956/what-is-up-with-the-source-code-license-on-stack-overflow * * Further, whatever rights I have accidentally retained unto my own person, I forfeit under the * [WTFPL](http://www.wtfpl.net/about/) to the extent that is compatible with the primary license and * does not contradict the primary license. Or whatever. :) * **** */ public class ClassEnumerator { private static final Logger log = java.util.logging.Logger.getLogger(Generator.class.getName()); private static Class<?> loadClass(String className) { try { return getClassLoader().loadClass(className); } catch (ClassNotFoundException e) { throw new RuntimeException("Unexpected ClassNotFoundException loading class '" + className + "'"); } } private static void processDirectory(File directory, String pkgname, ArrayList<Class<?>> classes, Function<String, Boolean> matcher) { if (!matcher.apply(directory.toString())) { return; } log.fine("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.fine("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, matcher); } } } private static void processJarfile(URL resource, String pkgname, ArrayList<Class<?>> classes, Function<String, Boolean> matcher) { String relPath = pkgname.replace('.', '/'); String resPath = resource.getPath(); String jarPath = resPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", ""); log.fine("Reading JAR file: '" + jarPath + "'"); JarFile jarFile; try { jarFile = new JarFile(jarPath); } catch (IOException e) { 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(); if (matcher.apply(entryName)) { String className = null; if (entryName.endsWith(".class") && entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) { className = entryName.replace('/', '.').replace('\\', '.').replace(".class", ""); } log.fine("JarEntry '" + entryName + "' => class '" + className + "'"); if (className != null) { classes.add(loadClass(className)); } } } } public static ArrayList<Class<?>> getClassesForPackage(String pkgname, Function<String, Boolean> matcher) { ArrayList<Class<?>> classes = new ArrayList<Class<?>>(); String relPath = pkgname.replace('.', '/'); // Get a File object for the package URL resource = getClassLoader().getResource(relPath); if (resource == null) { throw new RuntimeException("Unexpected problem: No resource for " + relPath); } log.fine("Package: '" + pkgname + "' becomes Resource: '" + resource.toString() + "'"); resource.getPath(); if(resource.toString().startsWith("jar:")) { processJarfile(resource, pkgname, classes, matcher); } else { processDirectory(new File(resource.getPath()), pkgname, classes, matcher); } return classes; } private static ClassLoader getClassLoader() { ClassLoader tccl = Thread.currentThread().getContextClassLoader(); return tccl == null ? ClassEnumerator.class.getClassLoader() : tccl; } }