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.sizes; 021 022import java.util.ArrayDeque; 023import java.util.Deque; 024 025import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 026import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029 030/** 031 * <p> 032 * Restricts the number of executable statements to a specified limit. 033 * </p> 034 * <ul> 035 * <li> 036 * Property {@code max} - Specify the maximum threshold allowed. 037 * Type is {@code int}. 038 * Default value is {@code 30}. 039 * </li> 040 * <li> 041 * Property {@code tokens} - tokens to check 042 * Type is {@code java.lang.String[]}. 043 * Validation type is {@code tokenSet}. 044 * Default value is: 045 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 046 * CTOR_DEF</a>, 047 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 048 * METHOD_DEF</a>, 049 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INSTANCE_INIT"> 050 * INSTANCE_INIT</a>, 051 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_INIT"> 052 * STATIC_INIT</a>, 053 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF"> 054 * COMPACT_CTOR_DEF</a>. 055 * </li> 056 * </ul> 057 * <p> 058 * To configure the check: 059 * </p> 060 * <pre> 061 * <module name="ExecutableStatementCount"/> 062 * </pre> 063 * <p> 064 * To configure the check with a threshold of 20 for constructor and method definitions: 065 * </p> 066 * <pre> 067 * <module name="ExecutableStatementCount"> 068 * <property name="max" value="20"/> 069 * <property name="tokens" value="CTOR_DEF,METHOD_DEF"/> 070 * </module> 071 * </pre> 072 * <p> 073 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 074 * </p> 075 * <p> 076 * Violation Message Keys: 077 * </p> 078 * <ul> 079 * <li> 080 * {@code executableStatementCount} 081 * </li> 082 * </ul> 083 * 084 * @since 3.2 085 */ 086@FileStatefulCheck 087public final class ExecutableStatementCountCheck 088 extends AbstractCheck { 089 090 /** 091 * A key is pointing to the warning message text in "messages.properties" 092 * file. 093 */ 094 public static final String MSG_KEY = "executableStatementCount"; 095 096 /** Default threshold. */ 097 private static final int DEFAULT_MAX = 30; 098 099 /** Stack of method contexts. */ 100 private final Deque<Context> contextStack = new ArrayDeque<>(); 101 102 /** Specify the maximum threshold allowed. */ 103 private int max; 104 105 /** Current method context. */ 106 private Context context; 107 108 /** Constructs a {@code ExecutableStatementCountCheck}. */ 109 public ExecutableStatementCountCheck() { 110 max = DEFAULT_MAX; 111 } 112 113 @Override 114 public int[] getDefaultTokens() { 115 return new int[] { 116 TokenTypes.CTOR_DEF, 117 TokenTypes.METHOD_DEF, 118 TokenTypes.INSTANCE_INIT, 119 TokenTypes.STATIC_INIT, 120 TokenTypes.SLIST, 121 TokenTypes.COMPACT_CTOR_DEF, 122 }; 123 } 124 125 @Override 126 public int[] getRequiredTokens() { 127 return new int[] {TokenTypes.SLIST}; 128 } 129 130 @Override 131 public int[] getAcceptableTokens() { 132 return new int[] { 133 TokenTypes.CTOR_DEF, 134 TokenTypes.METHOD_DEF, 135 TokenTypes.INSTANCE_INIT, 136 TokenTypes.STATIC_INIT, 137 TokenTypes.SLIST, 138 TokenTypes.COMPACT_CTOR_DEF, 139 }; 140 } 141 142 /** 143 * Setter to specify the maximum threshold allowed. 144 * 145 * @param max the maximum threshold. 146 */ 147 public void setMax(int max) { 148 this.max = max; 149 } 150 151 @Override 152 public void beginTree(DetailAST rootAST) { 153 context = new Context(null); 154 contextStack.clear(); 155 } 156 157 @Override 158 public void visitToken(DetailAST ast) { 159 switch (ast.getType()) { 160 case TokenTypes.CTOR_DEF: 161 case TokenTypes.METHOD_DEF: 162 case TokenTypes.INSTANCE_INIT: 163 case TokenTypes.STATIC_INIT: 164 case TokenTypes.COMPACT_CTOR_DEF: 165 visitMemberDef(ast); 166 break; 167 case TokenTypes.SLIST: 168 visitSlist(ast); 169 break; 170 default: 171 throw new IllegalStateException(ast.toString()); 172 } 173 } 174 175 @Override 176 public void leaveToken(DetailAST ast) { 177 switch (ast.getType()) { 178 case TokenTypes.CTOR_DEF: 179 case TokenTypes.METHOD_DEF: 180 case TokenTypes.INSTANCE_INIT: 181 case TokenTypes.STATIC_INIT: 182 case TokenTypes.COMPACT_CTOR_DEF: 183 leaveMemberDef(ast); 184 break; 185 case TokenTypes.SLIST: 186 // Do nothing 187 break; 188 default: 189 throw new IllegalStateException(ast.toString()); 190 } 191 } 192 193 /** 194 * Process the start of the member definition. 195 * 196 * @param ast the token representing the member definition. 197 */ 198 private void visitMemberDef(DetailAST ast) { 199 contextStack.push(context); 200 context = new Context(ast); 201 } 202 203 /** 204 * Process the end of a member definition. 205 * 206 * @param ast the token representing the member definition. 207 */ 208 private void leaveMemberDef(DetailAST ast) { 209 final int count = context.getCount(); 210 if (count > max) { 211 log(ast, MSG_KEY, count, max); 212 } 213 context = contextStack.pop(); 214 } 215 216 /** 217 * Process the end of a statement list. 218 * 219 * @param ast the token representing the statement list. 220 */ 221 private void visitSlist(DetailAST ast) { 222 if (context.getAST() != null) { 223 // find member AST for the statement list 224 final DetailAST contextAST = context.getAST(); 225 DetailAST parent = ast.getParent(); 226 int type = parent.getType(); 227 while (type != TokenTypes.METHOD_DEF 228 && !isConstructorOrInit(type)) { 229 parent = parent.getParent(); 230 type = parent.getType(); 231 } 232 if (parent == contextAST) { 233 context.addCount(ast.getChildCount() / 2); 234 } 235 } 236 } 237 238 /** 239 * Check if token type is a ctor (compact or canonical) or instance/ static initializer. 240 * 241 * @param tokenType type of token we are checking 242 * @return true if token type is constructor or initializer 243 */ 244 private static boolean isConstructorOrInit(int tokenType) { 245 return tokenType == TokenTypes.CTOR_DEF 246 || tokenType == TokenTypes.INSTANCE_INIT 247 || tokenType == TokenTypes.STATIC_INIT 248 || tokenType == TokenTypes.COMPACT_CTOR_DEF; 249 } 250 251 /** 252 * Class to encapsulate counting information about one member. 253 */ 254 private static class Context { 255 256 /** Member AST node. */ 257 private final DetailAST ast; 258 259 /** Counter for context elements. */ 260 private int count; 261 262 /** 263 * Creates new member context. 264 * 265 * @param ast member AST node. 266 */ 267 /* package */ Context(DetailAST ast) { 268 this.ast = ast; 269 count = 0; 270 } 271 272 /** 273 * Increase count. 274 * 275 * @param addition the count increment. 276 */ 277 public void addCount(int addition) { 278 count += addition; 279 } 280 281 /** 282 * Gets the member AST node. 283 * 284 * @return the member AST node. 285 */ 286 public DetailAST getAST() { 287 return ast; 288 } 289 290 /** 291 * Gets the count. 292 * 293 * @return the count. 294 */ 295 public int getCount() { 296 return count; 297 } 298 299 } 300 301}