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