ClassList.java
package org.djutils.test;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;
/**
* ClassList contains two helper methods that check whether all classes in a package implement a given interface or method.
* <p>
* Copyright (c) 2025-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
* for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
* distributed under a three-clause BSD-style license, which can be found at
* <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
* </p>
* @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
* @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
* @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
*/
public final class ClassList
{
/** */
private ClassList()
{
//
}
/**
* Return a list with the class names without an explicitly declared method. By default, the check ignores anonymous inner
* classes, but includes explicit local or static inner classes. It only looks at classes, not at interfaces, records,
* annotation classes or enums.
* @param methodName the method to check for
* @param packageNameList a list of package names to check
* @return a list of classes within the given packages without a toString() method
*/
public static List<String> classesWithoutMethod(final String methodName, final String... packageNameList)
{
List<String> result = new ArrayList<>();
try (ScanResult scanResult = new ClassGraph() // .verbose() logs activities
.overrideClasspath("target/classes") // don't include test classes
.enableAllInfo() // Scan classes, methods, fields, annotations
.acceptPackages(packageNameList) // Scan what's in pkg and subpackages (omit to scan all packages)
.scan())
{
scanResult.getAllClasses()
.stream()
.filter(ci -> !ci.isInterface() && !ci.isEnum() && !ci.isAnnotation() && !ci.isRecord())
.filter(ci -> !ci.isAnonymousInnerClass())
.filter(ci -> !ci.hasDeclaredMethod(methodName))
.forEach(classInfo ->
{ result.add(classInfo.getName()); });
}
return result;
}
/**
* Prints a list with the class names without an explicitly declared method. By default, the check ignores anonymous inner
* classes, but includes explicit local or static inner classes. It only looks at classes, not at interfaces, records,
* annotation classes or enums.
* @param methodName the method to check for
* @param packageNameList a list of package names to check
*/
public static void printClassesWithoutMethod(final String methodName, final String... packageNameList)
{
System.out.println("Classes without toString() method:");
classesWithoutMethod(methodName, packageNameList).forEach(System.out::println);
}
/**
* Return a list with the class names that do not implement the given interface. By default, the check ignores anonymous
* inner classes, but includes explicit local or static inner classes. It only looks at classes, not at interfaces, records,
* annotation classes or enums.
* @param interfaceClass the interface to check for
* @param packageNameList a list of package names to check
* @return a list of classes within the given packages that do not implement the provided interface
*/
public static List<String> classesWithoutInterface(final Class<?> interfaceClass, final String... packageNameList)
{
List<String> result = new ArrayList<>();
try (ScanResult scanResult = new ClassGraph() // .verbose() logs activities
.overrideClasspath("target/classes") // don't include test classes
.enableAllInfo() // Scan classes, methods, fields, annotations
.acceptPackages(packageNameList) // Scan what's in pkg and subpackages (omit to scan all packages)
.scan())
{
scanResult.getAllClasses()
.stream()
.filter(ci -> !ci.isInterface() && !ci.isEnum() && !ci.isAnnotation() && !ci.isRecord())
.filter(ci -> !ci.isAnonymousInnerClass())
.filter(ci -> !ci.implementsInterface(interfaceClass))
.forEach(classInfo ->
{ result.add(classInfo.getName()); });
}
return result;
}
/**
* Prints a list with the class names that do not implement the given interface. By default, the check ignores anonymous
* inner classes, but includes explicit local or static inner classes. It only looks at classes, not at interfaces, records,
* annotation classes or enums.
* @param interfaceClass the interface to check for
* @param packageNameList a list of package names to check
*/
public static void printClassesWithoutInterface(final Class<?> interfaceClass, final String... packageNameList)
{
System.out.println("Classes without interface " + interfaceClass.getName());
classesWithoutInterface(interfaceClass, packageNameList).forEach(System.out::println);
}
/**
* Walk the path names and make two lists: one of the classes that do not implement toString() and one of the classes that
* do not implement the interface Serializable.
* @param args can contain the package name(s) to inspect; org.djutils will be taken if the args are empty
*/
public static void main(final String... args)
{
printClassesWithoutMethod("toString", args.length > 0 ? args : new String[] {"org.djutils"});
System.out.println();
printClassesWithoutInterface(Serializable.class, args.length > 0 ? args : new String[] {"org.djutils"});
}
}