001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.utils; 021 022import java.io.IOException; 023import java.lang.reflect.Constructor; 024import java.lang.reflect.Modifier; 025import java.util.Collection; 026import java.util.Set; 027import java.util.stream.Collectors; 028 029import com.google.common.reflect.ClassPath; 030import com.puppycrawl.tools.checkstyle.TreeWalkerFilter; 031import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 032import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 033import com.puppycrawl.tools.checkstyle.api.AuditListener; 034import com.puppycrawl.tools.checkstyle.api.AutomaticBean; 035import com.puppycrawl.tools.checkstyle.api.BeforeExecutionFileFilter; 036import com.puppycrawl.tools.checkstyle.api.Filter; 037import com.puppycrawl.tools.checkstyle.api.RootModule; 038 039/** 040 * Contains utility methods for module reflection. 041 */ 042public final class ModuleReflectionUtil { 043 044 /** Prevent instantiation. */ 045 private ModuleReflectionUtil() { 046 } 047 048 /** 049 * Gets checkstyle's modules (directly, not recursively) in the given packages. 050 * 051 * @param packages the collection of package names to use 052 * @param loader the class loader used to load Checkstyle package names 053 * @return the set of checkstyle's module classes 054 * @throws IOException if the attempt to read class path resources failed 055 * @see #isCheckstyleModule(Class) 056 */ 057 public static Set<Class<?>> getCheckstyleModules( 058 Collection<String> packages, ClassLoader loader) throws IOException { 059 final ClassPath classPath = ClassPath.from(loader); 060 return packages.stream() 061 .flatMap(pkg -> classPath.getTopLevelClasses(pkg).stream()) 062 .map(ClassPath.ClassInfo::load) 063 .filter(ModuleReflectionUtil::isCheckstyleModule) 064 .collect(Collectors.toSet()); 065 } 066 067 /** 068 * Checks whether a class may be considered as a checkstyle module. Checkstyle's modules are 069 * non-abstract classes, which are either checkstyle's checks, file sets, filters, file filters, 070 * {@code TreeWalker} filters, audit listener, or root module. 071 * 072 * @param clazz class to check. 073 * @return true if the class may be considered as the checkstyle module. 074 */ 075 public static boolean isCheckstyleModule(Class<?> clazz) { 076 return isValidCheckstyleClass(clazz) 077 && (isCheckstyleTreeWalkerCheck(clazz) 078 || isFileSetModule(clazz) 079 || isFilterModule(clazz) 080 || isFileFilterModule(clazz) 081 || isTreeWalkerFilterModule(clazz) 082 || isAuditListener(clazz) 083 || isRootModule(clazz)); 084 } 085 086 /** 087 * Checks whether a class extends 'AutomaticBean', is non-abstract, and has a default 088 * constructor. 089 * 090 * @param clazz class to check. 091 * @return true if a class may be considered a valid production class. 092 */ 093 public static boolean isValidCheckstyleClass(Class<?> clazz) { 094 return AutomaticBean.class.isAssignableFrom(clazz) 095 && !Modifier.isAbstract(clazz.getModifiers()) 096 && hasDefaultConstructor(clazz) 097 && isNotXpathFileGenerator(clazz); 098 } 099 100 /** 101 * Checks if the class has a default constructor. 102 * 103 * @param clazz class to check 104 * @return true if the class has a default constructor. 105 */ 106 private static boolean hasDefaultConstructor(Class<?> clazz) { 107 boolean result = false; 108 for (Constructor<?> constructor : clazz.getDeclaredConstructors()) { 109 if (constructor.getParameterCount() == 0) { 110 result = true; 111 break; 112 } 113 } 114 return result; 115 } 116 117 /** 118 * Checks whether a class may be considered as the checkstyle check 119 * which has TreeWalker as a parent. 120 * Checkstyle's checks are classes which implement 'AbstractCheck' interface. 121 * 122 * @param clazz class to check. 123 * @return true if a class may be considered as the checkstyle check. 124 */ 125 public static boolean isCheckstyleTreeWalkerCheck(Class<?> clazz) { 126 return AbstractCheck.class.isAssignableFrom(clazz); 127 } 128 129 /** 130 * Checks whether a class may be considered as the checkstyle file set. 131 * Checkstyle's file sets are classes which implement 'AbstractFileSetCheck' interface. 132 * 133 * @param clazz class to check. 134 * @return true if a class may be considered as the checkstyle file set. 135 */ 136 public static boolean isFileSetModule(Class<?> clazz) { 137 return AbstractFileSetCheck.class.isAssignableFrom(clazz); 138 } 139 140 /** 141 * Checks whether a class may be considered as the checkstyle filter. 142 * Checkstyle's filters are classes which implement 'Filter' interface. 143 * 144 * @param clazz class to check. 145 * @return true if a class may be considered as the checkstyle filter. 146 */ 147 public static boolean isFilterModule(Class<?> clazz) { 148 return Filter.class.isAssignableFrom(clazz); 149 } 150 151 /** 152 * Checks whether a class may be considered as the checkstyle file filter. 153 * Checkstyle's file filters are classes which implement 'BeforeExecutionFileFilter' interface. 154 * 155 * @param clazz class to check. 156 * @return true if a class may be considered as the checkstyle file filter. 157 */ 158 public static boolean isFileFilterModule(Class<?> clazz) { 159 return BeforeExecutionFileFilter.class.isAssignableFrom(clazz); 160 } 161 162 /** 163 * Checks whether a class may be considered as the checkstyle audit listener module. 164 * Checkstyle's audit listener modules are classes which implement 'AuditListener' interface. 165 * 166 * @param clazz class to check. 167 * @return true if a class may be considered as the checkstyle audit listener module. 168 */ 169 public static boolean isAuditListener(Class<?> clazz) { 170 return AuditListener.class.isAssignableFrom(clazz); 171 } 172 173 /** 174 * Checks whether a class may be considered as the checkstyle root module. 175 * Checkstyle's root modules are classes which implement 'RootModule' interface. 176 * 177 * @param clazz class to check. 178 * @return true if a class may be considered as the checkstyle root module. 179 */ 180 public static boolean isRootModule(Class<?> clazz) { 181 return RootModule.class.isAssignableFrom(clazz); 182 } 183 184 /** 185 * Checks whether a class may be considered as the checkstyle {@code TreeWalker} filter. 186 * Checkstyle's {@code TreeWalker} filters are classes which implement 'TreeWalkerFilter' 187 * interface. 188 * 189 * @param clazz class to check. 190 * @return true if a class may be considered as the checkstyle {@code TreeWalker} filter. 191 */ 192 public static boolean isTreeWalkerFilterModule(Class<?> clazz) { 193 return TreeWalkerFilter.class.isAssignableFrom(clazz); 194 } 195 196 /** 197 * Checks whether a class is {@code XpathFileGeneratorAstFilter} or 198 * {@code XpathFileGeneratorAuditListener}. 199 * See issue #102 https://github.com/checkstyle/checkstyle/issues/102 200 * 201 * @param clazz class to check. 202 * @return true if a class name starts with `XpathFileGenerator`. 203 */ 204 private static boolean isNotXpathFileGenerator(Class<?> clazz) { 205 return !clazz.getSimpleName().startsWith("XpathFileGenerator"); 206 } 207}