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