6

As the description says: how to get a list of all Java class files for a given package name.

mythbu
  • 786
  • 1
  • 7
  • 20
  • This is not a question. Perhaps you should edit the question to a proper question and post the answer as an answer. – Eran Feb 23 '15 at 16:11
  • 1
    I know. I had some trouble with the Q&A-Feature. I only want to share that code snippet since this community helped me to work it out ... – mythbu Feb 23 '15 at 16:12

2 Answers2

14

I've seen many questions and ways here on SO and other sites to find all classes in a specific Java package. Most of the solutions didn't work for me. Sometimes they worked on Jar files but not on "plain" Java projects in a folder (like the way an IDE does it) or the other way around. So I put all those code snippets together and formed a solution which will work (for me) out of the box regardless if the code is inside a Jar file or in a plain folder structure.

It's really simple: you give the method getClassesInPackage the name of the package to inspect and you will get a list of all classes in this package. Currently no exception is "consumed" orderly.

Have fun with it! Here is the code:

public static final List<Class<?>> getClassesInPackage(String packageName) {
    String path = packageName.replaceAll("\\.", File.separator);
    List<Class<?>> classes = new ArrayList<>();
    String[] classPathEntries = System.getProperty("java.class.path").split(
            System.getProperty("path.separator")
    );

    String name;
    for (String classpathEntry : classPathEntries) {
        if (classpathEntry.endsWith(".jar")) {
            File jar = new File(classpathEntry);
            try {
                JarInputStream is = new JarInputStream(new FileInputStream(jar));
                JarEntry entry;
                while((entry = is.getNextJarEntry()) != null) {
                    name = entry.getName();
                    if (name.endsWith(".class")) {
                        if (name.contains(path) && name.endsWith(".class")) {
                            String classPath = name.substring(0, entry.getName().length() - 6);
                            classPath = classPath.replaceAll("[\\|/]", ".");
                            classes.add(Class.forName(classPath));
                        }
                    }
                }
            } catch (Exception ex) {
                // Silence is gold
            }
        } else {
            try {
                File base = new File(classpathEntry + File.separatorChar + path);
                for (File file : base.listFiles()) {
                    name = file.getName();
                    if (name.endsWith(".class")) {
                        name = name.substring(0, name.length() - 6);
                        classes.add(Class.forName(packageName + "." + name));
                    }
                }
            } catch (Exception ex) {
                // Silence is gold
            }
        }
    }

    return classes;
}
mythbu
  • 786
  • 1
  • 7
  • 20
  • 2
    Why the pokemon exceptions? – JacksOnF1re Feb 23 '15 at 16:13
  • The places where the exceptions are thrown one should insert own code depending on the usage of the code. It might not always be sufficient to exit the whole exploration only on a single instantiation error. One could output a debug message or do other useful stuff. – mythbu Feb 25 '15 at 10:01
  • Thanks, it works, even though it scans the entire classpath (which can take time). Fortunately, I use this code only for tests, so that's no big deal. – Alex Semeniuk Feb 28 '19 at 10:14
  • On Windows I had to change the separator in the "path" string variable in case of classpath entries within a jar: It should always be a forward slash, and not a backward slash as the current code suggests for a Windows platform. – Gérard Binkhorst Sep 15 '19 at 09:31
  • How can I change this to support nested packages? – Dhruv garg Nov 12 '20 at 06:01
  • To make it work on Windows, I had to change the second line to `String path = packageName.replaceAll("\\.", "\\" + File.separator);` – Yong Mar 12 '23 at 14:53
2

Here is @mythbu 's answer in Kotlin:

@Throws(Exception::class)
fun getClassesInPackage(packageName: String) = with(packageName.replace(".", File.separator)) {
    System.getProperty("java.class.path")
        .split(System.getProperty("path.separator").toRegex())
        .flatMap { classpathEntry ->
            if (classpathEntry.endsWith(".jar")) {
                JarInputStream(FileInputStream(File(classpathEntry))).use { s ->
                    generateSequence { s.nextJarEntry }
                            .map { it.name }
                            .filter { this in it && it.endsWith(".class") }
                            .map { it.substring(0, it.length - 6) }
                            .map { it.replace('|', '.').replace('/', '.') }
                            .map { Class.forName(it) }
                            .toList()
                }
            } else {
                File(classpathEntry, this).list()
                        ?.asSequence()
                        ?.filter { it.endsWith(".class") }
                        ?.map { it.substring(0, it.length - 6) }
                        ?.map { Class.forName("$packageName.$it") }
                        ?.toList() ?: emptyList()
            }
        }
}
Alex Semeniuk
  • 1,865
  • 19
  • 28