1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.TreeWalkerFilter;
31  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
32  import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
33  import com.puppycrawl.tools.checkstyle.api.AuditListener;
34  import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
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 (directly, not recursively) in the given packages.
50       * @param packages the collection of package names to use
51       * @param loader the class loader used to load Checkstyle package names
52       * @return the set of checkstyle's module classes
53       * @throws IOException if the attempt to read class path resources failed
54       * @see #isCheckstyleModule(Class)
55       */
56      public static Set<Class<?>> getCheckstyleModules(
57              Collection<String> packages, ClassLoader loader) throws IOException {
58          final ClassPath classPath = ClassPath.from(loader);
59          return packages.stream()
60                  .flatMap(pkg -> classPath.getTopLevelClasses(pkg).stream())
61                  .map(ClassPath.ClassInfo::load)
62                  .filter(ModuleReflectionUtil::isCheckstyleModule)
63                  .collect(Collectors.toSet());
64      }
65  
66      /**
67       * Checks whether a class may be considered as a checkstyle module. Checkstyle's modules are
68       * non-abstract classes, which are either checkstyle's checks, file sets, filters, file filters,
69       * {@code TreeWalker} filters, audit listener, or root module.
70       * @param clazz class to check.
71       * @return true if the class may be considered as the checkstyle module.
72       */
73      public static boolean isCheckstyleModule(Class<?> clazz) {
74          return isValidCheckstyleClass(clazz)
75              && (isCheckstyleTreeWalkerCheck(clazz)
76                      || isFileSetModule(clazz)
77                      || isFilterModule(clazz)
78                      || isFileFilterModule(clazz)
79                      || isTreeWalkerFilterModule(clazz)
80                      || isAuditListener(clazz)
81                      || isRootModule(clazz));
82      }
83  
84      /**
85       * Checks whether a class extends 'AutomaticBean', is non-abstract, and has a default
86       * constructor.
87       * @param clazz class to check.
88       * @return true if a class may be considered a valid production class.
89       */
90      public static boolean isValidCheckstyleClass(Class<?> clazz) {
91          return AutomaticBean.class.isAssignableFrom(clazz)
92                  && !Modifier.isAbstract(clazz.getModifiers())
93                  && hasDefaultConstructor(clazz)
94                  && isNotXpathFileGenerator(clazz);
95      }
96  
97      /**
98       * Checks if the class has a default constructor.
99       * @param clazz class to check
100      * @return true if the class has a default constructor.
101      */
102     private static boolean hasDefaultConstructor(Class<?> clazz) {
103         boolean result = false;
104         for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
105             if (constructor.getParameterCount() == 0) {
106                 result = true;
107                 break;
108             }
109         }
110         return result;
111     }
112 
113     /**
114      * Checks whether a class may be considered as the checkstyle check
115      * which has TreeWalker as a parent.
116      * Checkstyle's checks are classes which implement 'AbstractCheck' interface.
117      * @param clazz class to check.
118      * @return true if a class may be considered as the checkstyle check.
119      */
120     public static boolean isCheckstyleTreeWalkerCheck(Class<?> clazz) {
121         return AbstractCheck.class.isAssignableFrom(clazz);
122     }
123 
124     /**
125      * Checks whether a class may be considered as the checkstyle file set.
126      * Checkstyle's file sets are classes which implement 'AbstractFileSetCheck' interface.
127      * @param clazz class to check.
128      * @return true if a class may be considered as the checkstyle file set.
129      */
130     public static boolean isFileSetModule(Class<?> clazz) {
131         return AbstractFileSetCheck.class.isAssignableFrom(clazz);
132     }
133 
134     /**
135      * Checks whether a class may be considered as the checkstyle filter.
136      * Checkstyle's filters are classes which implement 'Filter' interface.
137      * @param clazz class to check.
138      * @return true if a class may be considered as the checkstyle filter.
139      */
140     public static boolean isFilterModule(Class<?> clazz) {
141         return Filter.class.isAssignableFrom(clazz);
142     }
143 
144     /**
145      * Checks whether a class may be considered as the checkstyle file filter.
146      * Checkstyle's file filters are classes which implement 'BeforeExecutionFileFilter' interface.
147      * @param clazz class to check.
148      * @return true if a class may be considered as the checkstyle file filter.
149      */
150     public static boolean isFileFilterModule(Class<?> clazz) {
151         return BeforeExecutionFileFilter.class.isAssignableFrom(clazz);
152     }
153 
154     /**
155      * Checks whether a class may be considered as the checkstyle audit listener module.
156      * Checkstyle's audit listener modules are classes which implement 'AuditListener' interface.
157      * @param clazz class to check.
158      * @return true if a class may be considered as the checkstyle audit listener module.
159      */
160     public static boolean isAuditListener(Class<?> clazz) {
161         return AuditListener.class.isAssignableFrom(clazz);
162     }
163 
164     /**
165      * Checks whether a class may be considered as the checkstyle root module.
166      * Checkstyle's root modules are classes which implement 'RootModule' interface.
167      * @param clazz class to check.
168      * @return true if a class may be considered as the checkstyle root module.
169      */
170     public static boolean isRootModule(Class<?> clazz) {
171         return RootModule.class.isAssignableFrom(clazz);
172     }
173 
174     /**
175      * Checks whether a class may be considered as the checkstyle {@code TreeWalker} filter.
176      * Checkstyle's {@code TreeWalker} filters are classes which implement 'TreeWalkerFilter'
177      * interface.
178      * @param clazz class to check.
179      * @return true if a class may be considered as the checkstyle {@code TreeWalker} filter.
180      */
181     public static boolean isTreeWalkerFilterModule(Class<?> clazz) {
182         return TreeWalkerFilter.class.isAssignableFrom(clazz);
183     }
184 
185     /**
186      * Checks whether a class is {@code XpathFileGeneratorAstFilter} or
187      * {@code XpathFileGeneratorAuditListener}.
188      * See issue #102 https://github.com/checkstyle/checkstyle/issues/102
189      * @param clazz class to check.
190      * @return true if a class name starts with `XpathFileGenerator`.
191      */
192     private static boolean isNotXpathFileGenerator(Class<?> clazz) {
193         return !clazz.getSimpleName().startsWith("XpathFileGenerator");
194     }
195 }