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