001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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 ModuleReflectionUtils { 043 044 /** Prevent instantiation. */ 045 private ModuleReflectionUtils() { 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(ModuleReflectionUtils::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 } 095 096 /** 097 * Checks if the class has a default constructor. 098 * @param clazz class to check 099 * @return true if the class has a default constructor. 100 */ 101 private static boolean hasDefaultConstructor(Class<?> clazz) { 102 boolean result = false; 103 for (Constructor<?> constructor : clazz.getDeclaredConstructors()) { 104 if (constructor.getParameterCount() == 0) { 105 result = true; 106 break; 107 } 108 } 109 return result; 110 } 111 112 /** 113 * Checks whether a class may be considered as the checkstyle check 114 * which has TreeWalker as a parent. 115 * Checkstyle's checks are classes which implement 'AbstractCheck' interface. 116 * @param clazz class to check. 117 * @return true if a class may be considered as the checkstyle check. 118 */ 119 public static boolean isCheckstyleTreeWalkerCheck(Class<?> clazz) { 120 return AbstractCheck.class.isAssignableFrom(clazz); 121 } 122 123 /** 124 * Checks whether a class may be considered as the checkstyle file set. 125 * Checkstyle's file sets are classes which implement 'AbstractFileSetCheck' interface. 126 * @param clazz class to check. 127 * @return true if a class may be considered as the checkstyle file set. 128 */ 129 public static boolean isFileSetModule(Class<?> clazz) { 130 return AbstractFileSetCheck.class.isAssignableFrom(clazz); 131 } 132 133 /** 134 * Checks whether a class may be considered as the checkstyle filter. 135 * Checkstyle's filters are classes which implement 'Filter' interface. 136 * @param clazz class to check. 137 * @return true if a class may be considered as the checkstyle filter. 138 */ 139 public static boolean isFilterModule(Class<?> clazz) { 140 return Filter.class.isAssignableFrom(clazz); 141 } 142 143 /** 144 * Checks whether a class may be considered as the checkstyle file filter. 145 * Checkstyle's file filters are classes which implement 'BeforeExecutionFileFilter' interface. 146 * @param clazz class to check. 147 * @return true if a class may be considered as the checkstyle file filter. 148 */ 149 public static boolean isFileFilterModule(Class<?> clazz) { 150 return BeforeExecutionFileFilter.class.isAssignableFrom(clazz); 151 } 152 153 /** 154 * Checks whether a class may be considered as the checkstyle audit listener module. 155 * Checkstyle's audit listener modules are classes which implement 'AuditListener' interface. 156 * @param clazz class to check. 157 * @return true if a class may be considered as the checkstyle audit listener module. 158 */ 159 public static boolean isAuditListener(Class<?> clazz) { 160 return AuditListener.class.isAssignableFrom(clazz); 161 } 162 163 /** 164 * Checks whether a class may be considered as the checkstyle root module. 165 * Checkstyle's root modules are classes which implement 'RootModule' interface. 166 * @param clazz class to check. 167 * @return true if a class may be considered as the checkstyle root module. 168 */ 169 public static boolean isRootModule(Class<?> clazz) { 170 return RootModule.class.isAssignableFrom(clazz); 171 } 172 173 /** 174 * Checks whether a class may be considered as the checkstyle {@code TreeWalker} filter. 175 * Checkstyle's {@code TreeWalker} filters are classes which implement 'TreeWalkerFilter' 176 * interface. 177 * @param clazz class to check. 178 * @return true if a class may be considered as the checkstyle {@code TreeWalker} filter. 179 */ 180 public static boolean isTreeWalkerFilterModule(Class<?> clazz) { 181 return TreeWalkerFilter.class.isAssignableFrom(clazz); 182 } 183 184}