View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.utils;
21  
22  import java.io.IOException;
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.Modifier;
25  import java.util.Collection;
26  import java.util.Set;
27  import java.util.stream.Collectors;
28  
29  import com.google.common.reflect.ClassPath;
30  import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean;
31  import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
32  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
33  import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
34  import com.puppycrawl.tools.checkstyle.api.AuditListener;
35  import com.puppycrawl.tools.checkstyle.api.BeforeExecutionFileFilter;
36  import com.puppycrawl.tools.checkstyle.api.Filter;
37  import com.puppycrawl.tools.checkstyle.api.RootModule;
38  
39  /**
40   * Contains utility methods for module reflection.
41   */
42  public final class ModuleReflectionUtil {
43  
44      /** Prevent instantiation. */
45      private ModuleReflectionUtil() {
46      }
47  
48      /**
49       * Gets checkstyle's modules classes (directly, not recursively) in the given packages.
50       *
51       * @param packages the collection of package names to use
52       * @param loader the class loader used to load Checkstyle package names
53       * @return the set of checkstyle's module classes
54       * @throws IOException if the attempt to read class path resources failed
55       * @see #isCheckstyleModule(Class)
56       */
57      public static Set<Class<?>> getCheckstyleModules(
58              Collection<String> packages, ClassLoader loader)
59                      throws IOException {
60          final ClassPath classPath = ClassPath.from(loader);
61          return packages.stream()
62                  .flatMap(pkg -> classPath.getTopLevelClasses(pkg).stream())
63                  .map(ClassPath.ClassInfo::load)
64                  .filter(ModuleReflectionUtil::isCheckstyleModule)
65                  .collect(Collectors.toUnmodifiableSet());
66      }
67  
68      /**
69       * Checks whether a class may be considered as a checkstyle module.
70       * Checkstyle's modules are classes which extend 'AutomaticBean', is
71       * non-abstract, and has a default constructor.
72       *
73       * @param clazz class to check.
74       * @return true if a class may be considered a valid production class.
75       */
76      public static boolean isCheckstyleModule(Class<?> clazz) {
77          return AbstractAutomaticBean.class.isAssignableFrom(clazz)
78                  && !Modifier.isAbstract(clazz.getModifiers())
79                  && hasDefaultConstructor(clazz)
80                  && isNotXpathFileGenerator(clazz);
81      }
82  
83      /**
84       * Checks if the class has a default constructor.
85       *
86       * @param clazz class to check
87       * @return true if the class has a default constructor.
88       */
89      private static boolean hasDefaultConstructor(Class<?> clazz) {
90          boolean result = false;
91          for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
92              if (constructor.getParameterCount() == 0) {
93                  result = true;
94                  break;
95              }
96          }
97          return result;
98      }
99  
100     /**
101      * Checks whether a class may be considered as the checkstyle check
102      * which has TreeWalker as a parent.
103      * Checkstyle's checks are classes which implement 'AbstractCheck' interface.
104      *
105      * @param clazz class to check.
106      * @return true if a class may be considered as the checkstyle check.
107      */
108     public static boolean isCheckstyleTreeWalkerCheck(Class<?> clazz) {
109         return AbstractCheck.class.isAssignableFrom(clazz);
110     }
111 
112     /**
113      * Checks whether a class may be considered as the checkstyle file set.
114      * Checkstyle's file sets are classes which implement 'AbstractFileSetCheck' interface.
115      *
116      * @param clazz class to check.
117      * @return true if a class may be considered as the checkstyle file set.
118      */
119     public static boolean isFileSetModule(Class<?> clazz) {
120         return AbstractFileSetCheck.class.isAssignableFrom(clazz);
121     }
122 
123     /**
124      * Checks whether a class may be considered as the checkstyle filter.
125      * Checkstyle's filters are classes which implement 'Filter' interface.
126      *
127      * @param clazz class to check.
128      * @return true if a class may be considered as the checkstyle filter.
129      */
130     public static boolean isFilterModule(Class<?> clazz) {
131         return Filter.class.isAssignableFrom(clazz);
132     }
133 
134     /**
135      * Checks whether a class may be considered as the checkstyle file filter.
136      * Checkstyle's file filters are classes which implement 'BeforeExecutionFileFilter' interface.
137      *
138      * @param clazz class to check.
139      * @return true if a class may be considered as the checkstyle file filter.
140      */
141     public static boolean isFileFilterModule(Class<?> clazz) {
142         return BeforeExecutionFileFilter.class.isAssignableFrom(clazz);
143     }
144 
145     /**
146      * Checks whether a class may be considered as the checkstyle audit listener module.
147      * Checkstyle's audit listener modules are classes which implement 'AuditListener' interface.
148      *
149      * @param clazz class to check.
150      * @return true if a class may be considered as the checkstyle audit listener module.
151      */
152     public static boolean isAuditListener(Class<?> clazz) {
153         return AuditListener.class.isAssignableFrom(clazz);
154     }
155 
156     /**
157      * Checks whether a class may be considered as the checkstyle root module.
158      * Checkstyle's root modules are classes which implement 'RootModule' interface.
159      *
160      * @param clazz class to check.
161      * @return true if a class may be considered as the checkstyle root module.
162      */
163     public static boolean isRootModule(Class<?> clazz) {
164         return RootModule.class.isAssignableFrom(clazz);
165     }
166 
167     /**
168      * Checks whether a class may be considered as the checkstyle {@code TreeWalker} filter.
169      * Checkstyle's {@code TreeWalker} filters are classes which implement 'TreeWalkerFilter'
170      * interface.
171      *
172      * @param clazz class to check.
173      * @return true if a class may be considered as the checkstyle {@code TreeWalker} filter.
174      */
175     public static boolean isTreeWalkerFilterModule(Class<?> clazz) {
176         return TreeWalkerFilter.class.isAssignableFrom(clazz);
177     }
178 
179     /**
180      * Checks whether a class is {@code XpathFileGeneratorAstFilter} or
181      * {@code XpathFileGeneratorAuditListener}.
182      * See issue <a href="https://github.com/checkstyle/checkstyle/issues/102">#102</a>
183      *
184      * @param clazz class to check.
185      * @return true if a class name starts with `XpathFileGenerator`.
186      */
187     private static boolean isNotXpathFileGenerator(Class<?> clazz) {
188         return !clazz.getSimpleName().startsWith("XpathFileGenerator");
189     }
190 
191 }