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.checks.metrics; 021 022import com.puppycrawl.tools.checkstyle.api.TokenTypes; 023 024/** 025 * <p> 026 * Checks the number of other classes a given class relies on. Also the square 027 * of this has been shown to indicate the amount of maintenance required 028 * in functional programs (on a file basis) at least. 029 * </p> 030 * <p> 031 * This check processes files in the following way: 032 * </p> 033 * <ol> 034 * <li> 035 * Iterates over all tokens that might contain type reference. 036 * </li> 037 * <li> 038 * If a class was imported with direct import (i.e. {@code import java.math.BigDecimal}), 039 * or the class was referenced with the package name (i.e. {@code java.math.BigDecimal value}) 040 * and the package was added to the {@code excludedPackages} parameter, 041 * the class does not increase complexity. 042 * </li> 043 * <li> 044 * If a class name was added to the {@code excludedClasses} parameter, 045 * the class does not increase complexity. 046 * </li> 047 * </ol> 048 * <ul> 049 * <li> 050 * Property {@code max} - Specify the maximum threshold allowed. 051 * Default value is {@code 20}. 052 * </li> 053 * <li> 054 * Property {@code excludedClasses} - Specify user-configured class names to ignore. 055 * Default value is {@code ArrayIndexOutOfBoundsException, ArrayList, Boolean, Byte, 056 * Character, Class, Deprecated, Deque, Double, Exception, Float, FunctionalInterface, 057 * HashMap, HashSet, IllegalArgumentException, IllegalStateException, 058 * IndexOutOfBoundsException, Integer, LinkedList, List, Long, Map, NullPointerException, 059 * Object, Override, Queue, RuntimeException, SafeVarargs, SecurityException, Set, Short, 060 * SortedMap, SortedSet, String, StringBuffer, StringBuilder, SuppressWarnings, Throwable, 061 * TreeMap, TreeSet, UnsupportedOperationException, Void, boolean, byte, char, double, 062 * float, int, long, short, void}. 063 * </li> 064 * <li> 065 * Property {@code excludeClassesRegexps} - Specify user-configured regular 066 * expressions to ignore classes. 067 * Default value is {@code ^$}. 068 * </li> 069 * <li> 070 * Property {@code excludedPackages} - Specify user-configured packages to ignore. 071 * All excluded packages should end with a period, so it also appends a dot to a package name. 072 * Default value is {@code {}}. 073 * </li> 074 * </ul> 075 * <p> 076 * To configure the check: 077 * </p> 078 * <pre> 079 * <module name="ClassFanOutComplexity"/> 080 * </pre> 081 * <p> 082 * Example: 083 * </p> 084 * <p> 085 * The check passes without violations in the following: 086 * </p> 087 * <pre> 088 * class InputClassComplexity { 089 * Set set = new HashSet(); // Set, HashSet ignored due to default excludedClasses property 090 * Map map = new HashMap(); // Map, HashMap ignored due to default excludedClasses property 091 * Date date = new Date(); // Counted, 1 092 * Time time = new Time(); // Counted, 2 093 * Place place = new Place(); // Counted, 3 094 * } 095 * </pre> 096 * <p> 097 * The check results in a violation in the following: 098 * </p> 099 * <pre> 100 * class InputClassComplexity { 101 * Set set = new HashSet(); // Set, HashSet ignored due to default excludedClasses property 102 * Map map = new HashMap(); // Map, HashMap ignored due to default excludedClasses property 103 * Date date = new Date(); // Counted, 1 104 * Time time = new Time(); // Counted, 2 105 * // mention of 18 other user defined classes 106 * Place place = new Place(); // violation, total is 21 107 * } 108 * </pre> 109 * <p> 110 * To configure the check with a threshold of 2: 111 * </p> 112 * <pre> 113 * <module name="ClassFanOutComplexity"> 114 * <property name="max" value="2"/> 115 * </module> 116 * </pre> 117 * <p> 118 * Example: 119 * </p> 120 * <p> 121 * The check passes without violations in the following: 122 * </p> 123 * <pre> 124 * class InputClassComplexity { 125 * Set set = new HashSet(); // Set, HashSet ignored due to default excludedClasses property 126 * Map map = new HashMap(); // Map, HashMap ignored due to default excludedClasses property 127 * Date date = new Date(); // Counted, 1 128 * Time time = new Time(); // Counted, 2 129 * } 130 * </pre> 131 * <p> 132 * The check results in a violation in the following: 133 * </p> 134 * <pre> 135 * class InputClassComplexity { 136 * Set set = new HashSet(); // Set, HashSet ignored due to default excludedClasses property 137 * Map map = new HashMap(); // Map, HashMap ignored due to default excludedClasses property 138 * Date date = new Date(); // Counted, 1 139 * Time time = new Time(); // Counted, 2 140 * Place place = new Place(); // violation, total is 3 141 * } 142 * </pre> 143 * <p> 144 * To configure the check with three excluded classes {@code HashMap}, 145 * {@code HashSet} and {@code Place}: 146 * </p> 147 * <pre> 148 * <module name="ClassFanOutComplexity"> 149 * <property name="excludedClasses" value="HashMap, HashSet, Place"/> 150 * </module> 151 * </pre> 152 * <p> 153 * Example: 154 * </p> 155 * <p> 156 * The check passes without violations in the following: 157 * </p> 158 * <pre> 159 * class InputClassComplexity { 160 * Set set = new HashSet(); // Set counted 1, HashSet ignored 161 * Map map = new HashMap(); // Map counted 2, HashMap ignored 162 * Date date = new Date(); // Counted, 3 163 * Time time = new Time(); // Counted, 4 164 * // mention of 16 other user defined classes 165 * Place place = new Place(); // Ignored 166 * } 167 * </pre> 168 * <p> 169 * The check results in a violation in the following: 170 * </p> 171 * <pre> 172 * class InputClassComplexity { 173 * Set set = new HashSet(); // Set counted 1, HashSet ignored 174 * Map map = new HashMap(); // Map counted 2, HashMap ignored 175 * Date date = new Date(); // Counted, 3 176 * Time time = new Time(); // Counted, 4 177 * // mention of 16 other user defined classes 178 * Space space = new Space(); // violation, total is 21 179 * } 180 * </pre> 181 * <p> 182 * To configure the check to exclude classes with a regular expression 183 * {@code .*Reader$}: 184 * </p> 185 * <pre> 186 * <module name="ClassFanOutComplexity"> 187 * <property name="excludeClassesRegexps" value=".*Reader$"/> 188 * </module> 189 * </pre> 190 * <p> 191 * Example: 192 * </p> 193 * <p> 194 * The check passes without violations in the following: 195 * </p> 196 * <pre> 197 * class InputClassComplexity { 198 * Set set = new HashSet(); // Set, HashSet ignored due to default excludedClasses property 199 * Map map = new HashMap(); // Map, HashMap ignored due to default excludedClasses property 200 * Date date = new Date(); // Counted, 1 201 * Time time = new Time(); // Counted, 2 202 * // mention of 18 other user defined classes 203 * BufferedReader br; // Ignored 204 * } 205 * </pre> 206 * <p> 207 * The check results in a violation in the following: 208 * </p> 209 * <pre> 210 * class InputClassComplexity { 211 * Set set = new HashSet(); // Set, HashSet ignored due to default excludedClasses property 212 * Map map = new HashMap(); // Map, HashMap ignored due to default excludedClasses property 213 * Date date = new Date(); // Counted, 1 214 * Time time = new Time(); // Counted, 2 215 * // mention of 18 other user defined classes 216 * File file; // violation, total is 21 217 * } 218 * </pre> 219 * <p> 220 * To configure the check with an excluded package {@code java.io}: 221 * </p> 222 * <pre> 223 * <module name="ClassFanOutComplexity"> 224 * <property name="excludedPackages" value="java.io"/> 225 * </module> 226 * </pre> 227 * <p> 228 * Example: 229 * </p> 230 * <p> 231 * The check passes without violations in the following: 232 * </p> 233 * <pre> 234 * import java.io.BufferedReader; 235 * 236 * class InputClassComplexity { 237 * Set set = new HashSet(); // Set, HashSet ignored due to default excludedClasses property 238 * Map map = new HashMap(); // Map, HashMap ignored due to default excludedClasses property 239 * Date date = new Date(); // Counted, 1 240 * Time time = new Time(); // Counted, 2 241 * // mention of 18 other user defined classes 242 * BufferedReader br; // Ignored 243 * } 244 * </pre> 245 * <p> 246 * The check results in a violation in the following: 247 * </p> 248 * <pre> 249 * import java.util.StringTokenizer; 250 * 251 * class InputClassComplexity { 252 * Set set = new HashSet(); // Set, HashSet ignored due to default excludedClasses property 253 * Map map = new HashMap(); // Map, HashMap ignored due to default excludedClasses property 254 * Date date = new Date(); // Counted, 1 255 * Time time = new Time(); // Counted, 2 256 * // mention of 18 other user defined classes 257 * StringTokenizer st; // violation, total is 21 258 * } 259 * </pre> 260 * <p> 261 * Override property {@code excludedPackages} to mark some packages as excluded. 262 * Each member of {@code excludedPackages} should be a valid identifier: 263 * </p> 264 * <ul> 265 * <li> 266 * {@code java.util} - valid, excludes all classes inside {@code java.util}, 267 * but not from the subpackages. 268 * </li> 269 * <li> 270 * {@code java.util.} - invalid, should not end with a dot. 271 * </li> 272 * <li> 273 * {@code java.util.*} - invalid, should not end with a star. 274 * </li> 275 * </ul> 276 * <p> 277 * Note, that checkstyle will ignore all classes from the {@code java.lang} 278 * package and its subpackages, even if the {@code java.lang} was not listed 279 * in the {@code excludedPackages} parameter. 280 * </p> 281 * <p> 282 * Also note, that {@code excludedPackages} will not exclude classes, imported 283 * via wildcard (e.g. {@code import java.math.*}). Instead of wildcard import 284 * you should use direct import (e.g. {@code import java.math.BigDecimal}). 285 * </p> 286 * <p> 287 * Also note, that checkstyle will not exclude classes within the same file even 288 * if it was listed in the {@code excludedPackages} parameter. 289 * For example, assuming the config is 290 * </p> 291 * <pre> 292 * <module name="ClassFanOutComplexity"> 293 * <property name="excludedPackages" value="a.b"/> 294 * </module> 295 * </pre> 296 * <p> 297 * And the file {@code a.b.Foo.java} is: 298 * </p> 299 * <pre> 300 * package a.b; 301 * 302 * import a.b.Bar; 303 * import a.b.c.Baz; 304 * 305 * class Foo { 306 * Bar bar; // Will be ignored, located inside ignored a.b package 307 * Baz baz; // Will not be ignored, located inside a.b.c package 308 * Data data; // Will not be ignored, same file 309 * 310 * class Data { 311 * Foo foo; // Will not be ignored, same file 312 * } 313 * } 314 * </pre> 315 * <p> 316 * The {@code bar} member will not be counted, since the {@code a.b} 317 * added to the {@code excludedPackages}. The {@code baz} member will be counted, 318 * since the {@code a.b.c} was not added to the {@code excludedPackages}. 319 * The {@code data} and {@code foo} members will be counted, as they are inside same file. 320 * </p> 321 * 322 * @since 3.4 323 */ 324public final class ClassFanOutComplexityCheck extends AbstractClassCouplingCheck { 325 326 /** 327 * A key is pointing to the warning message text in "messages.properties" 328 * file. 329 */ 330 public static final String MSG_KEY = "classFanOutComplexity"; 331 332 /** Default value of max value. */ 333 private static final int DEFAULT_MAX = 20; 334 335 /** Creates new instance of this check. */ 336 public ClassFanOutComplexityCheck() { 337 super(DEFAULT_MAX); 338 } 339 340 @Override 341 public int[] getRequiredTokens() { 342 return new int[] { 343 TokenTypes.PACKAGE_DEF, 344 TokenTypes.IMPORT, 345 TokenTypes.CLASS_DEF, 346 TokenTypes.EXTENDS_CLAUSE, 347 TokenTypes.IMPLEMENTS_CLAUSE, 348 TokenTypes.ANNOTATION, 349 TokenTypes.INTERFACE_DEF, 350 TokenTypes.ENUM_DEF, 351 TokenTypes.TYPE, 352 TokenTypes.LITERAL_NEW, 353 TokenTypes.LITERAL_THROWS, 354 TokenTypes.ANNOTATION_DEF, 355 }; 356 } 357 358 @Override 359 public int[] getAcceptableTokens() { 360 return getRequiredTokens(); 361 } 362 363 @Override 364 protected String getLogMessageId() { 365 return MSG_KEY; 366 } 367 368}