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