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 * @param packages the collection of package names to use 051 * @param loader the class loader used to load Checkstyle package names 052 * @return the set of checkstyle's module classes 053 * @throws IOException if the attempt to read class path resources failed 054 * @see #isCheckstyleModule(Class) 055 */ 056 public static Set<Class<?>> getCheckstyleModules( 057 Collection<String> packages, ClassLoader loader) throws IOException { 058 final ClassPath classPath = ClassPath.from(loader); 059 return packages.stream() 060 .flatMap(pkg -> classPath.getTopLevelClasses(pkg).stream()) 061 .map(ClassPath.ClassInfo::load) 062 .filter(ModuleReflectionUtil::isCheckstyleModule) 063 .collect(Collectors.toSet()); 064 } 065 066 /** 067 * Checks whether a class may be considered as a checkstyle module. Checkstyle's modules are 068 * non-abstract classes, which are either checkstyle's checks, file sets, filters, file filters, 069 * {@code TreeWalker} filters, audit listener, or root module. 070 * @param clazz class to check. 071 * @return true if the class may be considered as the checkstyle module. 072 */ 073 public static boolean isCheckstyleModule(Class<?> clazz) { 074 return isValidCheckstyleClass(clazz) 075 && (isCheckstyleTreeWalkerCheck(clazz) 076 || isFileSetModule(clazz) 077 || isFilterModule(clazz) 078 || isFileFilterModule(clazz) 079 || isTreeWalkerFilterModule(clazz) 080 || isAuditListener(clazz) 081 || isRootModule(clazz)); 082 } 083 084 /** 085 * Checks whether a class extends 'AutomaticBean', is non-abstract, and has a default 086 * constructor. 087 * @param clazz class to check. 088 * @return true if a class may be considered a valid production class. 089 */ 090 public static boolean isValidCheckstyleClass(Class<?> clazz) { 091 return AutomaticBean.class.isAssignableFrom(clazz) 092 && !Modifier.isAbstract(clazz.getModifiers()) 093 && hasDefaultConstructor(clazz) 094 && isNotXpathFileGenerator(clazz); 095 } 096 097 /** 098 * Checks if the class has a default constructor. 099 * @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}