View Javadoc
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 }