1 package org.djutils.test;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import io.github.classgraph.ClassGraph;
7 import io.github.classgraph.ScanResult;
8
9 /**
10 * ClassList contains two helper methods that check whether all classes in a package implement a given interface or method.
11 * <p>
12 * Copyright (c) 2025-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
13 * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
14 * distributed under a three-clause BSD-style license, which can be found at
15 * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
16 * </p>
17 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
18 * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
19 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
20 */
21 public final class ClassList
22 {
23 /** */
24 private ClassList()
25 {
26 //
27 }
28
29 /**
30 * Return a list with the class names without an explicitly declared method. By default, the check ignores anonymous inner
31 * classes, but includes explicit local or static inner classes. It only looks at classes, not at interfaces, records,
32 * annotation classes or enums.
33 * @param methodName the method to check for
34 * @param packageNameList a list of package names to check
35 * @return a list of classes within the given packages without a toString() method
36 */
37 public static List<String> classesWithoutMethod(final String methodName, final String... packageNameList)
38 {
39 List<String> result = new ArrayList<>();
40 try (ScanResult scanResult = new ClassGraph() // .verbose() logs activities
41 .overrideClasspath("target/classes") // don't include test classes
42 .enableAllInfo() // Scan classes, methods, fields, annotations
43 .acceptPackages(packageNameList) // Scan what's in pkg and subpackages (omit to scan all packages)
44 .scan())
45 {
46 scanResult.getAllClasses()
47 .stream()
48 .filter(ci -> !ci.isInterface() && !ci.isEnum() && !ci.isAnnotation() && !ci.isRecord())
49 .filter(ci -> !ci.isAnonymousInnerClass())
50 .filter(ci -> !ci.hasDeclaredMethod(methodName))
51 .forEach(classInfo ->
52 { result.add(classInfo.getName()); });
53 }
54 return result;
55 }
56
57 /**
58 * Prints a list with the class names without an explicitly declared method. By default, the check ignores anonymous inner
59 * classes, but includes explicit local or static inner classes. It only looks at classes, not at interfaces, records,
60 * annotation classes or enums.
61 * @param methodName the method to check for
62 * @param packageNameList a list of package names to check
63 */
64 public static void printClassesWithoutMethod(final String methodName, final String... packageNameList)
65 {
66 System.out.println("Classes without toString() method:");
67 classesWithoutMethod(methodName, packageNameList).forEach(System.out::println);
68 }
69
70 /**
71 * Return a list with the class names that do not implement the given interface. By default, the check ignores anonymous
72 * inner classes, but includes explicit local or static inner classes. It only looks at classes, not at interfaces, records,
73 * annotation classes or enums.
74 * @param interfaceClass the interface to check for
75 * @param packageNameList a list of package names to check
76 * @return a list of classes within the given packages that do not implement the provided interface
77 */
78 public static List<String> classesWithoutInterface(final Class<?> interfaceClass, final String... packageNameList)
79 {
80 List<String> result = new ArrayList<>();
81 try (ScanResult scanResult = new ClassGraph() // .verbose() logs activities
82 .overrideClasspath("target/classes") // don't include test classes
83 .enableAllInfo() // Scan classes, methods, fields, annotations
84 .acceptPackages(packageNameList) // Scan what's in pkg and subpackages (omit to scan all packages)
85 .scan())
86 {
87 scanResult.getAllClasses()
88 .stream()
89 .filter(ci -> !ci.isInterface() && !ci.isEnum() && !ci.isAnnotation() && !ci.isRecord())
90 .filter(ci -> !ci.isAnonymousInnerClass())
91 .filter(ci -> !ci.implementsInterface(interfaceClass))
92 .forEach(classInfo ->
93 { result.add(classInfo.getName()); });
94 }
95 return result;
96 }
97
98 /**
99 * Prints a list with the class names that do not implement the given interface. By default, the check ignores anonymous
100 * inner classes, but includes explicit local or static inner classes. It only looks at classes, not at interfaces, records,
101 * annotation classes or enums.
102 * @param interfaceClass the interface to check for
103 * @param packageNameList a list of package names to check
104 */
105 public static void printClassesWithoutInterface(final Class<?> interfaceClass, final String... packageNameList)
106 {
107 System.out.println("Classes without interface " + interfaceClass.getName());
108 classesWithoutInterface(interfaceClass, packageNameList).forEach(System.out::println);
109 }
110
111 /**
112 * Walk the path names and make a list of the classes that do not implement toString().
113 * @param args can contain the package name(s) to inspect; org.djutils will be taken if the args are empty
114 */
115 public static void main(final String... args)
116 {
117 printClassesWithoutMethod("toString", args.length > 0 ? args : new String[] {"org.djutils"});
118 }
119
120 }